Written by Aunoy Poddar July 21st, 2022

Process the puncta quantified raw data

current_file <- rstudioapi::getActiveDocumentContext()$path
output_file <- stringr::str_replace(current_file, '.Rmd', '.R')
knitr::purl(current_file, output = output_file)
file.edit(output_file)

Import packages and functions

library(Seurat)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Attaching SeuratObject
library(tictoc)
library(ggplot2)
library(patchwork)
library(pheatmap)
library(RColorBrewer)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble  3.1.8     ✔ dplyr   1.0.8
✔ tidyr   1.2.0     ✔ stringr 1.4.0
✔ readr   2.1.2     ✔ forcats 0.5.1
✔ purrr   0.3.4     
── Conflicts ───────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
library(png)
library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:patchwork’:

    align_plots
library(magick)
Linking to ImageMagick 6.9.10.23
Enabled features: fontconfig, freetype, fftw, lcms, pango, webp, x11
Disabled features: cairo, ghostscript, heic, raw, rsvg
Using 80 threads
library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
library(packcircles)
library(ggalt)
Registered S3 methods overwritten by 'ggalt':
  method                  from   
  grid.draw.absoluteGrob  ggplot2
  grobHeight.absoluteGrob ggplot2
  grobWidth.absoluteGrob  ggplot2
  grobX.absoluteGrob      ggplot2
  grobY.absoluteGrob      ggplot2
library(cccd)
Loading required package: igraph

Attaching package: ‘igraph’

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(stringr)
library(ggfortify)
Registered S3 method overwritten by 'ggfortify':
  method        from 
  fortify.table ggalt
library(circlize)
========================================
circlize version 0.4.15
CRAN page: https://cran.r-project.org/package=circlize
Github page: https://github.com/jokergoo/circlize
Documentation: https://jokergoo.github.io/circlize_book/book/

If you use it in published research, please cite:
Gu, Z. circlize implements and enhances circular visualization
  in R. Bioinformatics 2014.

This message can be suppressed by:
  suppressPackageStartupMessages(library(circlize))
========================================


Attaching package: ‘circlize’

The following object is masked from ‘package:igraph’:

    degree
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
library(igraph)
#library("leiden")

Load the data

data_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/rethresholded'
clump_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clumps'
meta_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/overlay'
output_dir_plot = '/home/aunoy/st/arc_profiling/st_analysis/results/plots'
output_dir_tbls = '/home/aunoy/st/arc_profiling/st_analysis/results/tables'

plot the meta data real quick

meta_ntrscts = read.csv(file.path(clump_dir, 'meta', 'META_ntrsct.csv'), header = FALSE) %>%
  as_tibble()

Load the data

df_408 <- load_slice(slice = "408")
df_408 <- clean_408(df_408)
df_408 <- embed_horizontal_408(df_408)

We have the coordinates for 408_TC and others

rownames(df_408) = 1:nrow(df_408)
jy_408 = df_408 %>%
  dplyr::select(-c(area, IMAGE.NAME, X_horz, Y_horz, clump)) %>%
  t() %>%
  CreateSeuratObject()
jy_408$image <- df_408$IMAGE.NAME

just set everything from below 1 in ratio to zero

jy_408 <- NormalizeData(jy_408, scale.factor = 1e5) ###
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
normed = GetAssayData(jy_408, slot = 'data')
normed[normed < 3] = 0
for(gene_name in rownames(jy_408)) {
  mdn_gene_expr = median(normed[gene_name, normed[gene_name, ] > 0])
}
jy_408 <- SetAssayData(jy_408, slot = 'data', normed)
jy_408 <- FindVariableFeatures(jy_408, selection.method = "vst")
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
all.genes <- rownames(jy_408)
jy_408 <- ScaleData(jy_408, features = all.genes)
Centering and scaling data matrix

  |                                                                                                   
  |                                                                                             |   0%
  |                                                                                                   
  |=============================================================================================| 100%
jy_408 <- RunPCA(jy_408, approx = FALSE)
Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.PC_ 1 
Positive:  VIP, ASCL1, CXCR4, RELN, MAF1, SATB2, GAD1, EMX1, KIA0319, DLX2 
       PAX6, CXCL12, LHX6, PROX1, SST, VLDLR 
Negative:  DCX, TBR1, LRP8, SCGN, COUPTF2, CXCL14, EGFR, TSHZ1, GSX2, EOMES 
       SP8, CALB2, NKX2.1, DCDC2, CXCR7, NCAM1 
PC_ 2 
Positive:  MAF1, NKX2.1, DLX2, GSX2, SST, TSHZ1, PROX1, GAD1, SP8, VIP 
       LHX6, CXCL14, SCGN, CXCR4, VLDLR, RELN 
Negative:  EOMES, TBR1, KIA0319, DCDC2, CALB2, SATB2, CXCL12, DCX, LRP8, EGFR 
       COUPTF2, PAX6, NCAM1, ASCL1, CXCR7, EMX1 
PC_ 3 
Positive:  RELN, SATB2, PAX6, MAF1, ASCL1, SST, SCGN, NCAM1, EMX1, GSX2 
       DCX, KIA0319, VIP, EGFR, CXCR7, TBR1 
Negative:  COUPTF2, GAD1, PROX1, DLX2, CXCR4, LRP8, LHX6, TSHZ1, CXCL14, EOMES 
       NKX2.1, CALB2, SP8, DCDC2, CXCL12, VLDLR 
PC_ 4 
Positive:  NCAM1, TSHZ1, VLDLR, PROX1, CXCR7, CXCL14, DCX, EGFR, ASCL1, DCDC2 
       PAX6, CALB2, SCGN, GAD1, EOMES, KIA0319 
Negative:  LHX6, LRP8, NKX2.1, TBR1, SP8, COUPTF2, DLX2, VIP, EMX1, GSX2 
       RELN, SST, MAF1, CXCL12, CXCR4, SATB2 
PC_ 5 
Positive:  SP8, GSX2, SCGN, NKX2.1, EGFR, CXCL14, COUPTF2, TSHZ1, EMX1, TBR1 
       PAX6, CALB2, SATB2, MAF1, NCAM1, CXCR7 
Negative:  CXCR4, SST, GAD1, VIP, EOMES, DLX2, DCDC2, RELN, PROX1, LHX6 
       VLDLR, DCX, ASCL1, CXCL12, KIA0319, LRP8 
jy_408 <- FindNeighbors(jy_408, dims = 1:30)
Computing nearest neighbor graph
Computing SNN
jy_408 <- FindClusters(jy_408, resolution = 0.8)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1190
Number of edges: 41891

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.6696
Number of communities: 7
Elapsed time: 0 seconds
jy_408 <- RunUMAP(jy_408, dims = 1:30)
Warning: The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session09:25:10 UMAP embedding parameters a = 0.9922 b = 1.112
09:25:10 Read 1190 rows and found 30 numeric columns
09:25:10 Using Annoy for neighbor search, n_neighbors = 30
09:25:10 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:25:10 Writing NN index file to temp file /tmp/Rtmpie36qI/file25503b1e881e8b
09:25:10 Searching Annoy index using 1 thread, search_k = 3000
09:25:11 Annoy recall = 100%
09:25:11 Commencing smooth kNN distance calibration using 1 thread
09:25:12 Initializing from normalized Laplacian + noise
09:25:12 Commencing optimization for 500 epochs, with 45048 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:25:14 Optimization finished
DimPlot(jy_408,  reduction = "umap", group.by = 'seurat_clusters') + NoAxes()

jy_408.markers <- FindAllMarkers(jy_408, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
Calculating cluster 0

  |                                                  | 0 % ~calculating  
  |++++++++++                                        | 20% ~00s          
  |++++++++++++++++++++                              | 40% ~00s          
  |++++++++++++++++++++++++++++++                    | 60% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 1

  |                                                  | 0 % ~calculating  
  |+++++                                             | 8 % ~00s          
  |+++++++++                                         | 17% ~00s          
  |+++++++++++++                                     | 25% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++                             | 42% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++                    | 58% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 2

  |                                                  | 0 % ~calculating  
  |+++++                                             | 8 % ~00s          
  |+++++++++                                         | 17% ~00s          
  |+++++++++++++                                     | 25% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++                             | 42% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++                    | 58% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 3

  |                                                  | 0 % ~calculating  
  |++++++                                            | 11% ~00s          
  |++++++++++++                                      | 22% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++++                           | 44% ~00s          
  |++++++++++++++++++++++++++++                      | 56% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 4

  |                                                  | 0 % ~calculating  
  |+++++++                                           | 12% ~00s          
  |+++++++++++++                                     | 25% ~00s          
  |+++++++++++++++++++                               | 38% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++++                  | 62% ~00s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 5

  |                                                  | 0 % ~calculating  
  |++++++++                                          | 14% ~00s          
  |+++++++++++++++                                   | 29% ~00s          
  |++++++++++++++++++++++                            | 43% ~00s          
  |+++++++++++++++++++++++++++++                     | 57% ~00s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 6

  |                                                  | 0 % ~calculating  
  |+++++                                             | 8 % ~00s          
  |+++++++++                                         | 17% ~00s          
  |+++++++++++++                                     | 25% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++                             | 42% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++                    | 58% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
jy_408.markers %>%
   group_by(cluster) %>%
   slice_max(n = 32, order_by = avg_log2FC)
NA
jy_408$clump = df_408$clump

DimPlot(jy_408,  reduction = "umap", group.by = 'clump') + NoAxes() + NoLegend()

hcoords = df_408 %>% dplyr::select(c('X_horz', 'Y_horz')) %>% as.matrix()
colnames(hcoords) <- c('pixel_1', 'pixel_2')

jy_408[["H"]] <- CreateDimReducObject(embeddings = hcoords, key = "pixel_", assay = DefaultAssay(jy_408))

Merge both datasets and generate a metadata column that corresponds

to the cell

df_164 <- load_slice(slice = "164")
df_164 <- clean_164(df_164)
df_164 <- embed_horizontal_164(df_164)

We have the coordinates for 408_TC and others

rownames(df_164) = 1:nrow(df_164)
jy_164 = df_164 %>%
  dplyr::select(-c(area, IMAGE.NAME, X, Y, clump)) %>%
  t() %>%
  CreateSeuratObject()
jy_164$image = df_164$IMAGE.NAME

just set everything from below 1 in ratio to zero

jy_164 <- NormalizeData(jy_164, scale.factor = 1e5) ###
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
normed = GetAssayData(jy_164, slot = 'data')
normed[normed < 3] = 0
for(gene_name in rownames(jy_164)) {
  mdn_gene_expr = median(normed[gene_name, normed[gene_name, ] > 0])
}
jy_164 <- SetAssayData(jy_164, slot = 'data', normed)
jy_164 <- FindVariableFeatures(jy_164, selection.method = "vst")
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
all.genes <- rownames(jy_164)
jy_164 <- ScaleData(jy_164, features = all.genes)
Centering and scaling data matrix

  |                                                                                                   
  |                                                                                             |   0%
  |                                                                                                   
  |=============================================================================================| 100%
jy_164 <- RunPCA(jy_164, approx = FALSE)
Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.PC_ 1 
Positive:  NKX2.1, MAF1, SCGN, PROX1, GAD1, SP8, VIP, LHX6, GSX2, DCX 
       TSHZ1, CXCR7, COUPTF2, SST, DLX2, LRP8 
Negative:  KIA0319, SATB2, ASCL1, PAX6, CALB2, DCDC2, RELN, NCAM1, CXCL12, TBR1 
       EMX1, EGFR, CXCL14, VLDLR, CXCR4, EOMES 
PC_ 2 
Positive:  DCX, SCGN, SP8, TBR1, EGFR, SATB2, DCDC2, EOMES, NKX2.1, LRP8 
       KIA0319, NCAM1, EMX1, CXCR7, TSHZ1, CALB2 
Negative:  GAD1, VIP, CXCR4, LHX6, MAF1, DLX2, SST, RELN, CXCL12, CXCL14 
       COUPTF2, PROX1, PAX6, GSX2, ASCL1, VLDLR 
PC_ 3 
Positive:  COUPTF2, SP8, PROX1, SCGN, EOMES, TBR1, MAF1, CXCR7, NKX2.1, GSX2 
       TSHZ1, LHX6, DCDC2, EGFR, VIP, LRP8 
Negative:  SST, ASCL1, PAX6, RELN, CALB2, CXCL14, EMX1, CXCL12, DLX2, NCAM1 
       GAD1, CXCR4, KIA0319, DCX, VLDLR, SATB2 
PC_ 4 
Positive:  TSHZ1, PROX1, SP8, ASCL1, DLX2, VLDLR, NKX2.1, NCAM1, PAX6, CXCR7 
       SCGN, EMX1, RELN, CXCL14, VIP, KIA0319 
Negative:  LRP8, TBR1, EOMES, CALB2, LHX6, GSX2, COUPTF2, DCX, MAF1, GAD1 
       CXCL12, DCDC2, SATB2, EGFR, CXCR4, SST 
PC_ 5 
Positive:  PAX6, COUPTF2, RELN, TSHZ1, DLX2, SP8, CXCL12, SCGN, TBR1, GSX2 
       LRP8, DCX, SATB2, LHX6, ASCL1, GAD1 
Negative:  NKX2.1, DCDC2, EOMES, VLDLR, NCAM1, SST, CXCR7, EMX1, CXCR4, EGFR 
       MAF1, KIA0319, VIP, PROX1, CXCL14, CALB2 
jy_164 <- FindNeighbors(jy_164, dims = 1:30)
Computing nearest neighbor graph
Computing SNN
jy_164 <- FindClusters(jy_164, resolution = 0.8)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 802
Number of edges: 30100

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.6519
Number of communities: 6
Elapsed time: 0 seconds
jy_164 <- RunUMAP(jy_164, dims = 1:30)
09:25:18 UMAP embedding parameters a = 0.9922 b = 1.112
09:25:18 Read 802 rows and found 30 numeric columns
09:25:18 Using Annoy for neighbor search, n_neighbors = 30
09:25:18 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:25:18 Writing NN index file to temp file /tmp/Rtmpie36qI/file25503b737aeae1
09:25:18 Searching Annoy index using 1 thread, search_k = 3000
09:25:18 Annoy recall = 100%
09:25:19 Commencing smooth kNN distance calibration using 1 thread
09:25:20 Initializing from normalized Laplacian + noise
09:25:20 Commencing optimization for 500 epochs, with 30056 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:25:21 Optimization finished
jy_164$clump <- df_164$clump

DimPlot(jy_164,  reduction = "umap", group.by = 'seurat_clusters') + NoAxes()

DimPlot(jy_164,  reduction = "umap", group.by = 'clump') + NoAxes() + NoLegend()

unique(df_164$IMAGE.NAME)
 [1] "CC_8"         "CC_10"        "CC_Cortical1" "CC_Cortical2" "CC_L2-1"      "CC_L2-2"     
 [7] "CC_L2-3"      "CC_2"         "CC_3"         "CC_4"         "CC_5"         "CC_6"        
[13] "CC_7"         "CC_9"         "TC_1"         "TC_2"         "TC_3"         "TC_4"        
[19] "TC_5"         "TC_6"         "TC_7"         "TC_8"         "TC_9"         "TC_10"       
[25] "TC_Cortical1" "TC_Cortical2" "TC_Cortical3"
images_ordered = c('TC_Cortical3', 'TC_Cortical2', 'TC_Cortical1', 'TC_10', 'TC_9', 'TC_8', 'TC_7', 'TC_6', 'TC_5', 'TC_4', 'TC_3', 'TC_2','TC_1','CC_2','CC_3',
           'CC_4', 'CC_5', 'CC_6', 'CC_7', 'CC_9', 'CC_8', 'CC_10',
           'CC_L2-1', 'CC_L2-2', 'CC_L2-3', 'CC_Cortical1', 'CC_Cortical2')
x_horz = 1:length(images_ordered) * 35
y_horz = rep(0, length(images_ordered))
horz_embedding = data.frame()
df_164$X_horz = -1
df_164$Y_horz = -1

images = list.files(meta_dir)
for(i in 1:length(images_ordered)){
    image_name = images_ordered[i]
    print(image_name)
    split_names = strsplit(image_name, '_')
    cortex = toupper(split_names[[1]][1])
    number = split_names[[1]][2]
    number_csv = paste0('_', number, '.csv')
    filename = images[grepl(cortex, images) & grepl(number_csv, images) & grepl('164', images)]
    coordinates = read.table(file.path(meta_dir, filename), sep = ',', header = TRUE)
    if(image_name == "CC_L2-1"){
      coordinates = coordinates[c(1:37, 39:nrow(coordinates)), ]
    }
    ## checked already that lists are equal, missing 1, 18, 19 for now, layer 1 and others
 
    ## so this is a little tricky, so need to get it right
    ## Remember, it is the top right that the coordinate is coming from, but
    ## the bottom right is the new coordinate space.
    ## so first when we get the original coordinate space, to set to relative
    ## of bottom would be the same X, but 1024 - Y
    
    ## push out the coordinates for better visualization
    #x_repelled <- (512 - coordinates$X_Coordinate_In_pixels)
    
    
    df_164[df_164$IMAGE.NAME == image_name, 'X_horz'] = (coordinates$X_Coordinate_In_pixels / 
                                                      IMAGE_SIZE * IMAGE_LEN) + y_horz[i]
    df_164[df_164$IMAGE.NAME == image_name, 'Y_horz'] = ((1024-coordinates$Y_Coordinate_In_pixels) / 
                                                      IMAGE_SIZE * IMAGE_LEN) + x_horz[i]
}
[1] "TC_Cortical3"
[1] "TC_Cortical2"
[1] "TC_Cortical1"
[1] "TC_10"
[1] "TC_9"
[1] "TC_8"
[1] "TC_7"
[1] "TC_6"
[1] "TC_5"
[1] "TC_4"
[1] "TC_3"
[1] "TC_2"
[1] "TC_1"
[1] "CC_2"
[1] "CC_3"
[1] "CC_4"
[1] "CC_5"
[1] "CC_6"
[1] "CC_7"
[1] "CC_9"
[1] "CC_8"
[1] "CC_10"
[1] "CC_L2-1"
[1] "CC_L2-2"
[1] "CC_L2-3"
[1] "CC_Cortical1"
[1] "CC_Cortical2"
hcoords = df_164 %>% dplyr::select(c('X_horz', 'Y_horz')) %>% as.matrix()
colnames(hcoords) <- c('pixel_1', 'pixel_2')

jy_164[["H"]] <- CreateDimReducObject(embeddings = hcoords, key = "pixel_", assay = DefaultAssay(jy_164))
jy_164<- RenameCells(jy_164, c(outer('164_', 1:ncol(jy_164), FUN=paste0)))
jy_164$area = df_164$area
jy_408<- RenameCells(jy_408, c(outer('408_', 1:ncol(jy_408), FUN=paste0)))
jy_408$area = df_408$area
jy_all <- merge(jy_164, jy_408)
jy_all <- NormalizeData(jy_all, scale.factor = 1e5) ###
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
normed = GetAssayData(jy_all, slot = 'data')
normed[normed < 3] = 0
for(gene_name in rownames(jy_all)) {
  if (gene_name == 'DCX'){
    mdn_gene_expr = 0.5
    print('skip dcx')
  } else if (!gene_name %in% c('COUPTF2', 'SP8')){
    mdn_gene_expr = median(normed[gene_name, normed[gene_name, ] > 0])
  }else{
    mdn_gene_expr = quantile(normed[gene_name, normed[gene_name, ] > 0], .40)
  }

  normed[gene_name, normed[gene_name, ] < mdn_gene_expr] = 0
}
[1] "skip dcx"
jy_all <- SetAssayData(jy_all, slot = 'data', normed)
jy_all <- FindVariableFeatures(jy_all, selection.method = "vst")
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
all.genes <- rownames(jy_all)
jy_all <- ScaleData(jy_all, features = all.genes)
Centering and scaling data matrix

  |                                                                                                   
  |                                                                                             |   0%
  |                                                                                                   
  |=============================================================================================| 100%
jy_all <- RunPCA(jy_all, approx = FALSE)
Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.Warning: Requested number is larger than the number of available items (32). Setting to 32.PC_ 1 
Positive:  DCX, KIA0319, TBR1, SATB2, EOMES, DCDC2, LRP8, EGFR, CALB2, NCAM1 
       VLDLR, ASCL1, PAX6, CXCL12, SCGN, COUPTF2 
Negative:  GAD1, VIP, LHX6, MAF1, CXCR4, SST, NKX2.1, DLX2, PROX1, TSHZ1 
       SP8, GSX2, RELN, CXCL14, EMX1, CXCR7 
PC_ 2 
Positive:  SCGN, TSHZ1, DCX, NKX2.1, SP8, CXCR7, PROX1, GSX2, COUPTF2, TBR1 
       LRP8, VLDLR, CXCL14, EGFR, MAF1, NCAM1 
Negative:  ASCL1, KIA0319, CXCR4, SATB2, CALB2, CXCL12, VIP, RELN, PAX6, GAD1 
       DLX2, DCDC2, SST, LHX6, EOMES, EMX1 
PC_ 3 
Positive:  NCAM1, TSHZ1, MAF1, VLDLR, PROX1, ASCL1, CXCR7, RELN, GSX2, CXCL14 
       PAX6, EGFR, EMX1, CXCR4, SP8, DCDC2 
Negative:  CALB2, TBR1, CXCL12, LRP8, DCX, SCGN, SST, DLX2, COUPTF2, GAD1 
       LHX6, SATB2, EOMES, VIP, KIA0319, NKX2.1 
PC_ 4 
Positive:  GAD1, LRP8, TBR1, VIP, EGFR, LHX6, CXCR4, DCDC2, NCAM1, EOMES 
       DCX, CXCL14, CXCR7, SST, MAF1, TSHZ1 
Negative:  SP8, DLX2, PAX6, SCGN, CALB2, NKX2.1, EMX1, ASCL1, COUPTF2, SATB2 
       CXCL12, KIA0319, GSX2, PROX1, VLDLR, RELN 
PC_ 5 
Positive:  TBR1, LHX6, LRP8, RELN, SP8, PAX6, COUPTF2, GSX2, SST, ASCL1 
       EGFR, EMX1, MAF1, DLX2, DCX, NKX2.1 
Negative:  CALB2, NCAM1, CXCL12, VLDLR, TSHZ1, PROX1, CXCR7, CXCL14, GAD1, DCDC2 
       KIA0319, CXCR4, SATB2, VIP, SCGN, EOMES 
jy_all <- FindNeighbors(jy_all, dims = 1:30)
Computing nearest neighbor graph
Computing SNN
jy_all <- FindClusters(jy_all, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1992
Number of edges: 66856

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8190
Number of communities: 7
Elapsed time: 0 seconds
jy_all <- RunUMAP(jy_all, dims = 1:30)
09:25:25 UMAP embedding parameters a = 0.9922 b = 1.112
09:25:25 Read 1992 rows and found 30 numeric columns
09:25:25 Using Annoy for neighbor search, n_neighbors = 30
09:25:25 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:25:25 Writing NN index file to temp file /tmp/Rtmpie36qI/file25503b4a9beeaf
09:25:25 Searching Annoy index using 1 thread, search_k = 3000
09:25:26 Annoy recall = 100%
09:25:26 Commencing smooth kNN distance calibration using 1 thread
09:25:27 Initializing from normalized Laplacian + noise
09:25:27 Commencing optimization for 500 epochs, with 77712 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:25:31 Optimization finished
DimPlot(jy_all,  reduction = "umap", group.by = 'seurat_clusters') + NoAxes()

DimPlot(jy_all,  reduction = "umap", group.by = 'area') + NoAxes() # NoLegend()

FeaturePlot(jy_all, c('CXCL12', 'CXCR4'), cells = which(FetchData(jy_all, 'GAD1') > 0), cols = c( '#F18F01', '#048BA8'), blend= TRUE) 
Warning: Only two colors provided, assuming specified are for features and agumenting with 'lightgrey' for double-negatives

new.cluster.ids = c('CGE/LGE',
                    'Ex',
                    'TBR1+ CGE',
                    'CALB2+DLX2+',
                    'VIP+GAD1+',
                    'SST+LHX6+',
                    'MGE')
names(new.cluster.ids) <- levels(jy_all)
jy_all <- RenameIdents(jy_all, new.cluster.ids)
## Need to extract as cell x position matrix (X, Y)
## Let's start with 5 neighbors
XY_164 = Embeddings(jy_164, 'H')
neighbors = 5

nn_graph = nng(XY_164, k = neighbors)
#XY_408 = Embeddings(jy_408, 'H')
avg_same = c()
avg_othr = c()
for(i in 1:ncol(jy_164)){
  ## so I am going to iterate through each cell
  ## Get the class identity of the cell
  class_cell = Idents(jy_164)[i]
  ## Get the cells that are the same
  nearest_neighbors = as.matrix(nn_graph[[i]])
  neighbor_idents = Idents(jy_164)[nearest_neighbors[[1]]]
  same_ixs = which(neighbor_idents == class_cell)
  ## Get the cells that are not the same
  othr_ixs = which(neighbor_idents != class_cell)
  ## Get the avreage distance of same
  same_obj = dist(XY_164[c(i, nearest_neighbors[[1]][same_ixs]), ])
  avg_same = c(avg_same, mean(same_obj[1:length(same_ixs)]))
  ## Get the avreage distance of not same
  othr_obj = dist(XY_164[c(i, nearest_neighbors[[1]][othr_ixs]), ])
  avg_othr = c(avg_othr, mean(same_obj[1:length(othr_ixs)]))
}
tc_csv = read_csv('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump/164_TC_3_1_assignments.csv', col_names = FALSE)
Rows: 13 Columns: 2── Column specification ──────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): X1, X2
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
tc_csv[1, 2].value
Error: unexpected symbol in "tc_csv[1, 2].value"
pre_str
[1] "[u'4', u'6']"
str1 <- gsub('\\]', '', gsub('\\[', '', pre_str))
str2 <- gsub('u', '', gsub("'", '', str1))
## Load from 
clumps_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump/clump_data/'

clump_num_df = data.frame(img= character(0), y= integer(0), z = integer(0))
colnames(clump_num_df) <- c('img', 'pixel_ext', 'clust_num')
prev_clump = 'temp'
tic()

for(file_name in list.files(clumps_dir)){
  ### If the file is an assignment file, skip
  if(grepl('assignment', file_name)){
    next
  }
  ### We need to isolate the name, pixel_ext, # of clusters
  split_name = strsplit(file_name, '_')
  clump_name = paste(split_name[[1]][1:3], collapse = "_")
  pixel_ext = as.integer(strsplit(split_name[[1]][4], '[.]')[[1]][1])
  ### Load the file
  clump_csv <- read_csv(file.path(clumps_dir, file_name))
  if(prev_clump != clump_name){
    prev_clump = clump_name
    print(clump_name)
  }
  num_clusters = nrow(clump_csv)
  clump_num_df <- clump_num_df %>%
  add_row(img = clump_name, pixel_ext = pixel_ext, clust_num = num_clusters)
}
[1] "164_CC_10"
[1] "164_CC_2"
[1] "164_CC_3"
[1] "164_CC_4"
[1] "164_CC_5"
[1] "164_CC_6"
[1] "164_CC_7"
[1] "164_CC_8"
[1] "164_CC_9"
[1] "164_CC_Cortical1"
[1] "164_CC_Cortical2"
[1] "164_CC_L2-1"
[1] "164_CC_L2-2"
[1] "164_CC_L2-3"
[1] "164_TC_1"
[1] "164_TC_10"
[1] "164_TC_2"
[1] "164_TC_3"
[1] "164_TC_4"
[1] "164_TC_5"
[1] "164_TC_6"
[1] "164_TC_7"
[1] "164_TC_8"
[1] "164_TC_9"
[1] "164_TC_Cortical1"
[1] "164_TC_Cortical2"
[1] "164_TC_Cortical3"
[1] "408_CC_10"
[1] "408_CC_11"
[1] "408_CC_12"
[1] "408_CC_4"
[1] "408_CC_5"
[1] "408_CC_6"
[1] "408_CC_7"
[1] "408_CC_8"
[1] "408_CC_9"
[1] "408_Cortical_1"
[1] "408_Cortical_2"
[1] "408_L2_1"
[1] "408_L2_2"
[1] "408_L2_3"
[1] "408_TC_10"
[1] "408_TC_11"
[1] "408_TC_12"
[1] "408_TC_13"
[1] "408_TC_14"
[1] "408_TC_15"
[1] "408_TC_16"
[1] "408_TC_17"
[1] "408_TC_2"
[1] "408_TC_20"
[1] "408_TC_3"
[1] "408_TC_4"
[1] "408_TC_5"
[1] "408_TC_6"
[1] "408_TC_7"
[1] "408_TC_8"
[1] "408_TC_9"
toc()
795.368 sec elapsed
## 1644 seconds it taeks, so about half an hour. This is worth saving lol
nrow(bro_test)
[1] 17
## Get the order
samp <- clump_num_df %>%
  filter(pixel_ext == 1) %>%
  arrange(clust_num)

order = samp$img
clump_num_df %>%
  #filter(pixel_ext <= 10) %>%
  arrange(clust_num) %>%
  ggplot(aes(x = img, y = clust_num, group = factor(pixel_ext))) + 
  geom_line(aes(color=factor(pixel_ext))) + 
  geom_point(aes(color=factor(pixel_ext))) + 
  scale_x_discrete(limits = order) + theme_classic() + RotatedAxis()


get_numeric_assignments <- function(assignment_str){
  str1 <- gsub('\\]', '', gsub('\\[', '', assignment_str))
  str2 <- gsub('u', '', gsub("'", '', str1))
  return(as.numeric(unlist(strsplit(str2, ','))))
}

get_assignments <- function(assignment_str, image_name){
  ### breakdown
  assignment_list <- get_numeric_assignments(assignment_str)
  if(length(assignment_list) == 0){
    return(-1)
  }
  if(grepl('164', image_name)){
    sdf <- df_164
    sobj <- jy_164
  }
  else{
    sdf <- df_408
    sobj <- jy_408
  }
  img_name_abbr = gsub('..._', '', image_name)
  cell_ixs <- which(sdf$IMAGE.NAME == img_name_abbr)
  image_sobj <- sobj[, cell_ixs] 
  cell_names <- colnames(image_sobj[, assignment_list])
  ## just return the names
  cells <- jy_all[, which(colnames(jy_all) %in% cell_names)]
  ## Will we get the same response each time?
  return(cells)
}
### So I am going to make a clump DF and also going to update jy_all
## Load from 
clumps_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump/clump_data/'
pixel_ext = '1'


features = c("...1","Area", "Perim.", "Major", "Minor", "Angle",
          "Circ.", "Feret", "%Area","MinFeret", "AR",
          "Round", "Solidity"  )

#jy_all$clump = "NaN"

clump_df = data.frame()
tic()
#[501:length(list.files(clumps_dir))]
for(file_name in list.files(clumps_dir)){
  ### If the file is an assignment file, skip
  if(!grepl(paste0('_', pixel_ext, '.csv'), file_name) || grepl('assignments', file_name)){ #|| grepl('164', file_name)){
    next
  }
  print(file_name)
  ### We need to isolate the name, pixel_ext, # of clusters
  split_name = strsplit(file_name, '_')
  clump_name = paste(split_name[[1]][1:3], collapse = "_")
  assignment_name = paste0(clump_name, '_', pixel_ext, '_assignments.csv')
  #pixel_ext = as.integer(strsplit(split_name[[1]][4], '[.]')[[1]][1])
  ### Load the file
  clump_name = gsub('Cortical_', 'Cortical', clump_name)
  clump_name = gsub('L2_', 'L2-', clump_name)
  clump_csv <- read_csv(file.path(clumps_dir, file_name))
  clump_csv$...1 <- as.character(clump_csv$...1)
  assignments_csv <- read_csv(file.path(clumps_dir, assignment_name), col_names = FALSE)
  
  temp <- data.frame()
  gtemp <- data.frame()
  ctype_names <- levels(Idents(jy_all))
  for(i in 1:nrow(clump_csv)){
    itr_name = paste0(clump_name, '_Clump_', i)
    clump_csv[i, 1] = itr_name
    assignment_str = assignments_csv[which(assignments_csv$X1 == itr_name), 2][[1]]
    cells = get_assignments(assignment_str, clump_name)
    ctypes <- matrix(0, 1, length(unique(Idents(jy_all))))
    clump_expr <- matrix(NaN, 1, length(rownames(jy_all)))
    if(class(cells) != "numeric"){
      for(j in 1:ncol(cells)) {
        ctype = which(ctype_names == Idents(cells)[[j]])
        ctypes[ctype] = ctypes[ctype] + 1
      }
      clump_expr <- rowMeans(as.matrix(GetAssayData(cells, 'data')))
      jy_all$clump[which(colnames(jy_all) %in% colnames(cells))] = clump_name
    } 
  else{
      colnames(clump_expr) <- colnames(gtemp)
    }
    temp <- rbind(temp, ctypes)
    ### This is to get average expression in clump
    gtemp <- rbind(gtemp, clump_expr)
  }
  colnames(temp) <- ctype_names
  colnames(gtemp) <- rownames(jy_all)
  clump_csv_to_add <- clump_csv %>% dplyr::select(features)
  clump_df <- rbind(clump_df, cbind(clump_csv_to_add, temp, gtemp))
  #print(assignment_name)
  #df_408 <- rbind(df_408, df_to_append)
  ### Gotta make sure that the image names are the same, there are some discrepancies
}
[1] "164_CC_10_1.csv"
[1] "164_CC_2_1.csv"
[1] "164_CC_3_1.csv"
[1] "164_CC_4_1.csv"
[1] "164_CC_5_1.csv"
[1] "164_CC_6_1.csv"
[1] "164_CC_7_1.csv"
[1] "164_CC_8_1.csv"
[1] "164_CC_9_1.csv"
[1] "164_CC_Cortical1_1.csv"
[1] "164_CC_Cortical2_1.csv"
[1] "164_CC_L2-1_1.csv"
[1] "164_CC_L2-2_1.csv"
[1] "164_CC_L2-3_1.csv"
[1] "164_TC_1_1.csv"
[1] "164_TC_10_1.csv"
[1] "164_TC_2_1.csv"
[1] "164_TC_3_1.csv"
[1] "164_TC_4_1.csv"
[1] "164_TC_5_1.csv"
[1] "164_TC_6_1.csv"
[1] "164_TC_7_1.csv"
[1] "164_TC_8_1.csv"
[1] "164_TC_9_1.csv"
[1] "164_TC_Cortical1_1.csv"
[1] "164_TC_Cortical2_1.csv"
[1] "164_TC_Cortical3_1.csv"
[1] "408_CC_10_1.csv"
[1] "408_CC_11_1.csv"
[1] "408_CC_12_1.csv"
[1] "408_CC_4_1.csv"
[1] "408_CC_5_1.csv"
[1] "408_CC_6_1.csv"
[1] "408_CC_7_1.csv"
[1] "408_CC_8_1.csv"
[1] "408_CC_9_1.csv"
[1] "408_Cortical_1_1.csv"
[1] "408_Cortical_2_1.csv"
[1] "408_L2_1_1.csv"
[1] "408_L2_2_1.csv"
[1] "408_L2_3_1.csv"
[1] "408_TC_10_1.csv"
[1] "408_TC_11_1.csv"
[1] "408_TC_12_1.csv"
[1] "408_TC_13_1.csv"
[1] "408_TC_14_1.csv"
[1] "408_TC_15_1.csv"
[1] "408_TC_16_1.csv"
[1] "408_TC_17_1.csv"
[1] "408_TC_2_1.csv"
[1] "408_TC_20_1.csv"
[1] "408_TC_3_1.csv"
[1] "408_TC_4_1.csv"
[1] "408_TC_5_1.csv"
[1] "408_TC_6_1.csv"
[1] "408_TC_7_1.csv"
[1] "408_TC_8_1.csv"
[1] "408_TC_9_1.csv"
## Save this difficultly made file
saveRDS(clump_df, file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump', 'clump_df.rds'))
toc()
198.761 sec elapsed
clump_df_408 <- readRDS(file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump', 'clump_df_408.rds'))
temporary
clump_df <- rbind(clump_df_164, clump_df_408)
Error in rbind(deparse.level, ...) : object 'clump_df_408' not found
saveRDS(clump_df, file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump', 'clump_df.rds'))
readRDS(clump_df_164, file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump', 'clump_df_164.rds'))
Error in readRDS(clump_df_164, file.path("/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump",  : 
  object 'clump_df_164' not found
clump_df <- rbind(clump_df_164, clump_df)

START HERE

clump_df <- readRDS(file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/clump', 'clump_df.rds'))
clump_df %>%
  ggplot(aes(x = Area, y = Perim.)) + geom_point()
clump_pca <- prcomp(clump_df[, 14:20], center = TRUE,scale. = TRUE)

summary(clump_pca)
library(ggfortify)
autoplot(clump_pca)
autoplot(clump_pca, data = clump_df, colour = 'stream')
clump_df$stream = "wrong"
clump_df$stream[which(grepl('408_TC', clump_df$...1))] = "408_TC"
clump_df$stream[which(grepl('164_TC', clump_df$...1))] = "164_TC"
clump_df$stream[which(grepl('408_CC', clump_df$...1))] = "408_CC"
clump_df$stream[which(grepl('164_CC', clump_df$...1))] = "164_CC"
clump_df$stream[which(grepl('408_Cortical', clump_df$...1))] = "PCort"
clump_df$stream[which(grepl('164_TC_Cortical', clump_df$...1))] = "ATCort"
clump_df$stream[which(grepl('164_CC_Cortical', clump_df$...1))] = "ACCort"
clump_df$stream[which(grepl('408_L2', clump_df$...1))] = "PL2"
clump_df$stream[which(grepl('164_CC_L2', clump_df$...1))] = "AL2"
clump_df %>%
  ggplot(aes(x = stream, y = Area)) + geom_violin()

How many clumps are there?

How big are these clumps?

What percent of DCX positive cells are in clumps?

asummary()
clump_df %>% group_by(stream) %>%
summarize()
clump_df %>%
  ggplot(aes(x = stream, y = Area)) + geom_violin()
clump_df$img <- str_split_fixed(clump_df$...1, "_Clump", 2)[, 1]
clump_df %>%
  ggplot(aes(x = stream, y = Area, color = 'TBR1+ CGE')) + geom_point(size = clump_df$Area / 10e4, ) + RotatedAxis()

subset_df <- clump_df %>%
  filter(grepl('_', stream))

clump_df %>%
  filter(grepl('_', stream)) %>%
  ggplot(aes(x = stream, y = ncell)) + geom_boxplot() + geom_point(size = subset_df$Area / 10e4, ) + RotatedAxis() + ylab('# DCX cells in clump')
clump_df %>%
  filter(grepl('_', stream)) %>%
  ggplot(aes(x = stream, y = Area)) + geom_boxplot() +  RotatedAxis()
library(umap)
clump_umap <- umap(clump_pca$x)
umap_cds <- as.data.frame(clump_umap$layout)
colnames(umap_cds) <- c('umap1', 'umap2')

clump_df$umap1 <- umap_cds$umap1
clump_df$umap2 <- umap_cds$umap2
clump_df %>%
  filter(umap2 < 10) %>%
  arrange(CALB2_pct) %>%
  ggplot(aes(x = umap1, y = umap2, color = CALB2_pct)) + geom_point(size = 0.8, alpha = 0.8)
clump_df %>%
  #filter(grepl('_', stream)) %>%
  ggplot(aes(x = umap1, y = umap2, color = stream)) + geom_point(size = 0.8, alpha = 0.5)
clump_df$ncell <- rowSums(clump_df[, 14:20])
clump_df$MGE_pct <- clump_df$MGE / clump_df$ncell
clump_df$TBR1_pct <- clump_df$`TBR1+ CGE` / clump_df$ncell
clump_df$Ex_pct <- clump_df$Ex / clump_df$ncell
clump_df$SST_pct <- clump_df$`SST+LHX6+` / clump_df$ncell
clump_df$CALB2_pct <- clump_df$`CALB2+DLX2+` / clump_df$ncell
clump_df$VIP_pct <- clump_df$`VIP+GAD1+` / clump_df$ncell
clump_df$CGE_pct <- clump_df$`CGE/LGE` / clump_df$ncell
weird_cluster <- clump_df %>%
  filter(umap2 > 10)
weird_cluster
clump_df$MGE_pct <- clump_df$MGE / clump_df$ncell
clump_df$TBR1_pct <- clump_df$`TBR1+ CGE` / clump_df$ncell
clump_df$Ex_pct <- clump_df$Ex / clump_df$ncell
clump_df$SST_pct <- clump_df$`SST+LHX6+` / clump_df$ncell
clump_df$CALB2_pct <- clump_df$`CALB2+DLX2+` / clump_df$ncell
clump_df$VIP_pct <- clump_df$`VIP+GAD1+` / clump_df$ncell
clump_df$CGE_pct <- clump_df$`CGE/LGE` / clump_df$ncell
#clump_df$order = hclust_avg$order
bplot_df <- clump_df[hclust_avg$order, c(1, 21,25, 26:32)] %>%
  filter(MGE_pct != 'NaN') %>%
  pivot_longer(!c(...1, stream, ncell), names_to = "pct_type", values_to = "pct")
bplot_df  %>%
  filter(ncell > 5) %>%
  #filter(grepl('164', stream)) %>%
  ggplot(aes(x = ...1, y = pct, fill = pct_type)) +
  geom_bar(position="stack", stat="identity")  +
  theme(axis.text.x=element_blank()) + xlab('clump') #remove x axis labels) + xl
dist_mat <- dist(clump_pca$x, method = 'euclidean')
hclust_avg <- hclust(dist_mat, method = 'average')
plot(hclust_avg)
avg_same = c()
avg_othr = c()
for(i in 1:ncol(clump_df)){
  ## so I am going to iterate through each cell
  ## Get the class identity of the cell
  class_cell = Idents(jy_164)[i]
  ## Get the cells that are the same
  nearest_neighbors = as.matrix(nn_graph[[i]])
  neighbor_idents = Idents(jy_164)[nearest_neighbors[[1]]]
  same_ixs = which(neighbor_idents == class_cell)
  ## Get the cells that are not the same
  othr_ixs = which(neighbor_idents != class_cell)
  ## Get the avreage distance of same
  same_obj = dist(XY_164[c(i, nearest_neighbors[[1]][same_ixs]), ])
  avg_same = c(avg_same, mean(same_obj[1:length(same_ixs)]))
  ## Get the avreage distance of not same
  othr_obj = dist(XY_164[c(i, nearest_neighbors[[1]][othr_ixs]), ])
  avg_othr = c(avg_othr, mean(same_obj[1:length(othr_ixs)]))
}
clump_df$max_pct <- clump_df[, 26:32] %>%
  apply(1, max)
clump_df$hmg_ix <- (clump_df$max_pct - (1/7)) / (1-1/7)
clump_df %>%
  filter(!is.na(hmg_ix)) %>%
  filter(ncell > 3) %>%
ggplot(aes(x=hmg_ix)) + 
  geom_histogram(color="black", fill="white")
clump_3 <- clump_df %>% filter(ncell>3)
nctyp = 7
btstrp = 1000

null_dist = matrix(0, nrow = btstrp, ncol = nrow(clump_3))
tic()
for(k in 1:btstrp){
  if(k %% 250 == 0){
    print(k)
  }
  for(i in 1:nrow(clump_3)){
     cells = clump_3$ncell[i]
     samp <- rdunif(cells, 1 , 7)
     mx_pct <- max(table(samp)) / cells
     null_dist[k, i] = (mx_pct - (1/7)) / (1-1/7)
  }
}
toc()
null_dist
hist(null_dist)
null_df <- as.data.frame(as.vector(colMeans(null_dist)))
colnames(null_df) <- 'ixs'
null_df$dist = 'null'
null_df
true_df <- as.data.frame(cbind(clump_3$hmg_ix, 'true'))
colnames(true_df) <- c('ixs', 'dist')
mg_df <- rbind(null_df, true_df)
mg_df$ixs <- as.numeric(mg_df$ixs)
mg_df %>%
  ggplot(aes(x=ixs, color=dist)) +
  geom_histogram(fill="white",  alpha=0.4, position="identity")
jy_all$clump
jy_all$in_clump <- 'Not in Clump'
jy_all$in_clump[which(jy_all$clump != "NaN")] <- 'In Clump'

jy_all$clump_stream <- paste(jy_all$area, jy_all$in_clump)

jy_all$avp = 'wrong'
jy_all$avp[which(grepl('164', jy_all$area))] = '164'
jy_all$avp[which(grepl('408', jy_all$area))] = '408'
jy_all$clump_stream2 <- paste(jy_all$avp, jy_all$in_clump)
jy_all$clump_plot = jy_all$clump
jy_all$clump_plot[which(jy_all$clump_type != "True Clump")] = "NaN"

Define the specific clump types


linear = c("164_TC_10_12",
"164_TC_10_5",
"164_TC_1_1",
"164_TC_5_4",
"164_TC_7_5",
"164_TC_8_7",
"164_TC_9_1",
"164_TC_9_2",
"408_CC_12_5",
"408_TC_10_5",
"408_TC_9_5")

psuedolinear = c("408_Cortical_1_0",
"408_TC_10_3",
"164_TC_1_3",
"164_TC_Cortical1_11",
"164_CC_8_2",
"164_CC_9_3",
"164_CC_L2-1_5",
"164_CC_L2-2_4",
"164_TC_8_2",
"164_TC_8_3",
"164_TC_2_1",
"164_TC_4_9",
"164_TC_5_2",
"164_TC_5_9",
"164_TC_6_0",
"164_TC_7_5",
"164_TC_7_1",
"164_TC_7_4",
"164_TC_8_4",
"408_TC_14_4",
"408_TC_2_0",
"408_TC_2_12",
"408_TC_3_3",
"408_TC_4_11",
"408_TC_4_16",
"408_tc_4_1",
"408_TC_6_5",
"164_TC_Cortical_2_1")

big_clump = c("408_TC_7_4",
"164_TC_1_0",
"164_TC_3_1",
"408_TC_11_1",
"408_TC_12_0",
"408_TC_13_0",
"408_TC_14_0",
"408_TC_5_3",
"408_TC_15_2",
"408_TC_3_1",
"408_TC_3_0",
"164_TC_6_6",
"408_TC_11_1",
"164_TC_Cortical1_5")
jy_all$clump_type = "NaN"
jy_all$clump_type[which(jy_all$clump != "NaN")] = "Small Clump"
jy_all$clump_type[which(jy_all$clump %in% linear)] = "Linear"
jy_all$clump_type[which(jy_all$clump %in% psuedolinear)] = "Psuedo-Linear"
jy_all$clump_type[which(jy_all$clump %in% big_clump)] = "True Clump"

jy_all$clump_reduced = 'error'
jy_all$clump_reduced[which(jy_all$clump_type %in% c('True Clump'))] = 'Clump'
jy_all$clump_reduced[which(jy_all$clump_type %in% c('NaN', 'Small Clump', 'Linear', 'Psuedo-Linear'))] = 'Individual'
dp <- DotPlot(jy_all, features = c('CXCR4', 'CXCR7', 'EGFR', 'LRP8', 'VLDLR'), group.by = "clump_reduced") + RotatedAxis() + ylab("")

ggsave(plot = dp, filename = 'clump_dot_plot.pdf', path = file.path(output_dir_plot, '20221212_1'), width = 12, height = 5, units = 'cm', dpi = 300)
jy_all$clump_stream <- paste(jy_all$area, jy_all$clump_type)
jy_all$clump_ctype <- paste(Ident(jy_all), jy_all$clump_reduced)
Error in Ident(jy_all) : could not find function "Ident"
#DotPlot(jy_all[, which(grepl("TC", jy_all$area) & grepl("164", jy_all$area))], features = c('CXCR4', 'LRP8', 'CXCR7', 'VLDLR', 'EGFR', 'CXCL12', 'CXCL14', 'RELN'), group.by = "clump_ctype") + RotatedAxis() + coord_flip()
DotPlot(jy_all, features = c('CXCR4', 'LRP8', 'CXCR7', 'VLDLR', 'EGFR', 'CXCL12', 'CXCL14', 'RELN'), group.by = "clump_ctype") + RotatedAxis() + coord_flip()

dp <- DotPlot(jy_all[, which(grepl('MS', jy_all$area))], features = c('CXCR4', 'LRP8', 'CXCR7', 'VLDLR', 'EGFR'), group.by = "clump_stream") + RotatedAxis() + ylab("")
ggsave(plot = dp, filename = 'clump_dot_plot_typestream.pdf', path = file.path(output_dir_plot, '20221212_1'), width = 15, height = 15, units = 'cm', dpi = 300)

Do a chord diagram that you like

from to value

1 a A 1

2 b B 2

3 c C 3

So for me, I need from cell type 1 to cell type 2

Basically

from to. value CT1. CT1 CT1 CT2 CT1 CT3

dp <- DotPlot(jy_all[, which(grepl('MS', jy_all$area))], features = c('CXCR4', 'LRP8', 'CXCR7', 'VLDLR', 'EGFR'), group.by = "clump_type") + RotatedAxis() + ylab("")
ggsave(plot = dp, filename = 'clump_dot_plot_type.pdf', path = file.path(output_dir_plot, '20221212_1'), width = 12, height = 10, units = 'cm', dpi = 300)
classes <- levels(Idents(jy_all))
n = length(classes)
c1 = rep(classes, n)
c2 = rep(classes, each=n)
c_df <- as.data.frame(cbind(c1, c2))
XY_164 = Embeddings(jy_164, 'H')
neighbors = 5

nn_graph = nng(XY_164, k = neighbors)

XY_408 = Embeddings(jy_408, 'H')
nn_graph2 = nng(XY_408, k = neighbors)

clusters_164 = Idents(jy_all)[1:ncol(jy_164)]
Idents(jy_164) = clusters_164
clusters_408 = Idents(jy_all)[(ncol(jy_164)+1):ncol(jy_all)]
Idents(jy_408) = clusters_408

fill_adj_matrix <- function(nn_graph, jy_obj, adj_mat, undirected = TRUE){
  adj_mat$val = 0
  added = as.data.frame(matrix(-1, 1, 2))
  colnames(added) = c('c1', 'c2')
  for(i in 1:ncol(jy_obj)){
    class_cell = Idents(jy_obj)[i]
    ## Get the cells that are the same
    nearest_neighbors = as.matrix(nn_graph[[i]])
    neighbor_idents = Idents(jy_obj)[nearest_neighbors[[1]]]
    for(n in 1:length(neighbor_idents)){
      #print(c(i, n))
      n_id = as.numeric(nearest_neighbors[[1]][n])
      neighbor = neighbor_idents[n]
      if(length(added$c2[which(added$c1 == i)] == n_id) > 0 &&
         added$c2[which(added$c1 == i)] == n_id && undirected
         ){
          #print('dup')
       } else{
         #print('yup')
        value = adj_mat$val[which(adj_mat$c1 == class_cell & adj_mat$c2 == neighbor)]
        adj_mat$val[which(adj_mat$c1 == class_cell & adj_mat$c2 == neighbor)] = value + 1
        added <- added %>% add_row(c1 = n_id, c2 = i)
      }
    }
  }
  return(adj_mat)
}

norm_adj_mat <- function(jy_obj, adj_mat){
  idents <- levels(Idents(jy_obj))
  for(ident in idents){
     id_ct <- sum(Idents(jy_obj) == ident) * neighbors
     adj_mat[which(adj_mat$c1 == ident), 3] = as.numeric(adj_mat[which(adj_mat$c1 == ident), 
                                                          3]) / id_ct
  }
  return(adj_mat)
}


adj_mat1 <- fill_adj_matrix(nn_graph, jy_164, c_df)
adj_mat2 <- fill_adj_matrix(nn_graph2, jy_408, c_df)

#adj_mat1p <-norm_adj_mat(jy_164, adj_mat1)
#adj_mat2p <-norm_adj_mat(jy_408, adj_mat2)
adj_mat_final <- as.data.frame(cbind(c1, c2, adj_mat1$val + adj_mat2$val))
#adj_mat_final <- norm_adj_mat(jy_all, adj_mat_final)
colnames(adj_mat_final) <- c('from', 'to', 'value')
adj_mat_final$value <- as.numeric(adj_mat_final$value)

df2 = data.frame(start = c("a", "b", "c", "a"), end = c("a", "a", "b", "c"))
chordDiagram(df2, grid.col = 1:3, self.link = 1)
title("self.link = 1")

chordDiagram(df2, grid.col = 1:3, self.link = 2)
title("self.link = 2")

mat <- dcast(adj_mat_final, from ~ to, value.var = "value") %>%
  column_to_rownames("from") %>%
  #mutate_all(.funs = readr::parse_number) %>%
  as.matrix() 
circos.clear()
chordDiagram(mat, self.link = 1, grid.col = get_cluster_colors(rownames(mat)), col = col_mat)

col_mat = matrix("#e5e5e5", nrow = nrow(mat), ncol = ncol(mat))
diag(col_mat) <- get_cluster_colors(rownames(mat))
#dim(col_mat) = dim(mat)  # to make sure it is a matrix
XYd_164 = Embeddings(jy_164[, which(grepl('CC', jy_164$area) & grepl('MS', jy_164$area))], 'H')
XYv_164 = Embeddings(jy_164[, which(grepl('TC', jy_164$area) & grepl('MS', jy_164$area))], 'H')

nnd_graph = nng(XYd_164, k = neighbors)
nnv_graph = nng(XYv_164, k = neighbors)

XYd_408 = Embeddings(jy_408[, which(grepl('CC', jy_408$area) & grepl('MS', jy_408$area))], 'H')
XYv_408 = Embeddings(jy_408[, which(grepl('TC', jy_408$area) & grepl('MS', jy_408$area))], 'H')

nnd_graph2 = nng(XYd_408, k = neighbors)
nnv_graph2 = nng(XYv_408, k = neighbors)
stream = 'CC'

adj_mat1 <- fill_adj_matrix(nn_graph, 
                            jy_164[, which(grepl(stream, jy_164$area) & grepl('MS', jy_164$area))], 
                            c_df, undirected = FALSE)
adj_mat2 <- fill_adj_matrix(nn_graph2, 
                            jy_408[, which(grepl(stream, jy_408$area) & grepl('MS', jy_408$area))], 
                            c_df, undirected = FALSE)
adj_mat_final <- as.data.frame(cbind(c1, c2, adj_mat1$val + adj_mat2$val))
#adj_mat_final <- norm_adj_mat(jy_all[, which(grepl(stream, jy_all$area) & grepl('MS', jy_all$area))], adj_mat_final)
colnames(adj_mat_final) <- c('from', 'to', 'value')
adj_mat_final$value = as.numeric(adj_mat_final$value)
check_adj_prob <- function(adj_mat){
  types = unique(adj_mat$from)
  for(ct in types){
    sm <- sum(adj_mat[which(adj_mat$from == ct), 3])
    print(sm)
  }
}
check_adj_prob(adj_mat_final)
[1] 0.9828729
[1] 0.9936508
[1] 0.9698113
[1] 1
[1] 0.9974359
[1] 1
[1] 1
mat <- dcast(adj_mat_final, from ~ to, value.var = "value") %>%
  column_to_rownames("from") %>%
  #mutate_all(.funs = readr::parse_number) %>%
  as.matrix() 

circos.clear()
chordDiagram(mat, self.link = 1, grid.col = get_cluster_colors(rownames(mat)), col = col_mat)

Okay, so I need a nodeID for each “unique type”, so just the cell types

### Need to set up the d: edges
### and set the vertices: which are the node IDs
#adj_mat_final
net <- graph_from_adjacency_matrix(mat, mode = "directed", weighted = TRUE)

V(net)$color <- get_cluster_colors(V(net)$name)

V(net)$size <- log(round(rowSums(mat) / neighbors)) * 7

# The labels are currently node IDs.
# Setting them to NA will render no labels:
V(net)$label <- NA

# Set edge width based on weight:
E(net)$width <- log(E(net)$weight)

#change arrow size and edge color:
E(net)$arrow.size <- .2
E(net)$edge.color <- "gray80"

# We can even set the network layout:
graph_attr(net, "layout") <- layout_with_fr(net)#net, center = 'CGE/LGE')
plot(net, edge.arrow.size=.4, edge.curved=.5)

First maek a heatmap from images and cell types

jy_all$slice <- NA
jy_all$slice[1:ncol(jy_164)] <- '164'
jy_all$slice[(ncol(jy_164)+1):ncol(jy_all)] <- '408'
jy_all$imgslice <- paste0(jy_all$slice, "_", jy_all$image)

ids <- levels(Idents(jy_all))
imgs <- unique(jy_all$imgslice)
jy_matrix <- matrix(-1, length(imgs), length(ids))
for(i in 1:length(imgs)){
  img <- imgs[i]
  obj <- jy_all[, which(jy_all$imgslice == img)]
  tb <- table(factor(Idents(obj), levels = ids))
  jy_matrix[i, ] <- tb / sum(tb)
}
rownames(jy_matrix) <- imgs
colnames(jy_matrix) <- ids
pheatmap(t(jy_matrix), cellwidth = 10, cellheight = 10, fontsize = 10,
         cluster_cols = FALSE, cluster_rows = FALSE)

Before permutation, lets just confirm that all neighbors are in same image

We have found that 69 cells have neighbors in other images. This should not be the case and should be fixed in following analyses.


same_img_check <- function(nn_graph, jy_obj){
  counter = 0
  for(i in 1:ncol(jy_obj)){
    cell_img = jy_obj$image[i]
    ## Get the cells that are the same
    nearest_neighbors = as.matrix(nn_graph[[i]])
    neighbor_imgs = jy_obj$image[nearest_neighbors[[1]]]
    if(any(neighbor_imgs != cell_img)){
      counter = counter + 1
    }
  }
  return(counter)
}
print(same_img_check(nn_graph, jy_164))
[1] 69

Need to run a permutation test


get_interactions <- function(knn_graph, obj, classes, c_df){
    c_df$val = 0
    for(i in 1:ncol(obj)){
      class_cell = Idents(obj)[i]
      ## Get the cells that are the same
      nearest_neighbors = as.matrix(nn_graph[[i]])
      neighbor_idents = Idents(obj)[nearest_neighbors[[1]]]
      for(neighbor in neighbor_idents){
        value = c_df$val[which(c_df$c1 == class_cell & c_df$c2 == neighbor)]
        c_df$val[which(c_df$c1 == class_cell & c_df$c2 == neighbor)] = value + 1
      }
    }
    return(c_df)
}
### Iterate through all images
### run 100 permutations, and get the p value from the real data
permutations = 99
neighbors = 5

### Need to set up the dataframe
classes <- levels(Idents(jy_all))
n = length(classes)
c1 = rep(classes, n)
c2 = rep(classes, each=n)
c_df <- as.data.frame(cbind(c1, c2))

## will save all into 2 different matrices
interaction_mat <- matrix(-1, nrow(c_df), length(imgs)) 
avoidance_mat <- matrix(-1, nrow(c_df), length(imgs)) 

## Need to create a jy_all embedding
embed_164 <- Embeddings(jy_164, "H")
embed_408 <- Embeddings(jy_408, "H")
jy_all[["H"]] <- CreateDimReducObject(embeddings = rbind(embed_164, embed_408), key = "pixel_", assay = DefaultAssay(jy_all))

for(i in 1:length(imgs)){
  print(paste("Permuting image", i, "of", length(imgs)))
  img <- imgs[i]
  obj <- jy_all[, which(jy_all$imgslice == img)]
  ### Create nn graph for real
  XY_obj = Embeddings(obj, 'H')
  nn_graph = nng(XY_obj, k = neighbors)
  ### Iterate through each cell type
  ### Compute the mean for this one
  data_cts <- get_interactions(nn_graph, obj, classes, c_df)
  obj_idents <- Idents(obj)
  perm_matrix = matrix(NA, nrow(c_df), permutations)
  for(permutation in 1:permutations){
    Idents(obj) <- sample(Idents(obj))
    perm_cts <- get_interactions(nn_graph, obj, classes, c_df)
    perm_matrix[, permutation] <- perm_cts$val
  }
  interaction_mat[, i] <- rowSums(perm_matrix <= data_cts$val) / (permutations + 1)
  avoidance_mat[, i] <- rowSums(perm_matrix >= data_cts$val) / (permutations + 1)
  ## count the identities
  tbl <- table(Idents(obj))
  ## If the cell type does not exist in the image, do not count the P value for the
  ## interaction... obviously
  nonexist_ixs <- which(!(c_df$c1 %in% names(tbl)) | !(c_df$c2 %in% names(tbl)))
  interaction_mat[nonexist_ixs, i] <- NA
  avoidance_mat[nonexist_ixs, i] <- NA
}
[1] "Permuting image 1 of 61"
[1] "Permuting image 2 of 61"
[1] "Permuting image 3 of 61"
[1] "Permuting image 4 of 61"
[1] "Permuting image 5 of 61"
[1] "Permuting image 6 of 61"
[1] "Permuting image 7 of 61"
[1] "Permuting image 8 of 61"
[1] "Permuting image 9 of 61"
[1] "Permuting image 10 of 61"
[1] "Permuting image 11 of 61"
[1] "Permuting image 12 of 61"
[1] "Permuting image 13 of 61"
[1] "Permuting image 14 of 61"
[1] "Permuting image 15 of 61"
[1] "Permuting image 16 of 61"
[1] "Permuting image 17 of 61"
[1] "Permuting image 18 of 61"
[1] "Permuting image 19 of 61"
[1] "Permuting image 20 of 61"
[1] "Permuting image 21 of 61"
[1] "Permuting image 22 of 61"
[1] "Permuting image 23 of 61"
[1] "Permuting image 24 of 61"
[1] "Permuting image 25 of 61"
[1] "Permuting image 26 of 61"
[1] "Permuting image 27 of 61"
[1] "Permuting image 28 of 61"
[1] "Permuting image 29 of 61"
[1] "Permuting image 30 of 61"
[1] "Permuting image 31 of 61"
[1] "Permuting image 32 of 61"
[1] "Permuting image 33 of 61"
[1] "Permuting image 34 of 61"
[1] "Permuting image 35 of 61"
[1] "Permuting image 36 of 61"
[1] "Permuting image 37 of 61"
[1] "Permuting image 38 of 61"
[1] "Permuting image 39 of 61"
[1] "Permuting image 40 of 61"
[1] "Permuting image 41 of 61"
[1] "Permuting image 42 of 61"
[1] "Permuting image 43 of 61"
[1] "Permuting image 44 of 61"
[1] "Permuting image 45 of 61"
[1] "Permuting image 46 of 61"
[1] "Permuting image 47 of 61"
[1] "Permuting image 48 of 61"
[1] "Permuting image 49 of 61"
[1] "Permuting image 50 of 61"
[1] "Permuting image 51 of 61"
[1] "Permuting image 52 of 61"
[1] "Permuting image 53 of 61"
[1] "Permuting image 54 of 61"
[1] "Permuting image 55 of 61"
[1] "Permuting image 56 of 61"
[1] "Permuting image 57 of 61"
[1] "Permuting image 58 of 61"
[1] "Permuting image 59 of 61"
[1] "Permuting image 60 of 61"
[1] "Permuting image 61 of 61"
## Save this difficultly made files
saveRDS(interaction_mat, file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/', 'interaction_mat.rds'))
saveRDS(avoidance_mat, file.path('/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/', 'avoidance_mat.rds'))
plot_map <- interaction_mat
plot_map[which(plot_map >= 0.05)] = NA
pheatmap(plot_map, cluster_cols = FALSE, cluster_rows = FALSE, cex = 0.8)

0.05
[1] 0.05
min(rowMeans(interaction_mat, na.rm = TRUE))
[1] 0.3
rownames(avoidance_mat) <- paste0(c_df$c1, "->", c_df$c2)
colnames(avoidance_mat) <- imgs
rownames(interaction_mat) <- paste0(c_df$c1, "->", c_df$c2)
colnames(interaction_mat) <- imgs
vms_labels <- c(paste0("164_", antr_TC_MS), paste0("408_", post_TC_MS))
dms_labels <- c(paste0("164_", antr_CC_MS), paste0("408_", post_CC_MS))

sub_mat <- interaction_mat[, dms_labels]
avgs <- rowMeans(sub_mat < 0.05, na.rm = TRUE)
#avgs
interaction_df <- c_df %>% add_column(value = avgs)
colnames(interaction_df) <- c('from', 'to', 'value')
mat <- dcast(interaction_df, from ~ to, value.var = "value") %>%
  column_to_rownames("from") %>%
  #mutate_all(.funs = readr::parse_number) %>%
  as.matrix() 
net <- graph_from_adjacency_matrix(mat, mode = "directed", weighted = TRUE)

V(net)$color <- get_cluster_colors(V(net)$name)

V(net)$size <- table(Idents(jy_all[, which(jy_all$imgslice %in% dms_labels)]))[V(net)$name] / 5

# The labels are currently node IDs.
# Setting them to NA will render no labels:
V(net)$label <- NA

# Set edge width based on weight:
E(net)$width <- E(net)$weight * 15

#change arrow size and edge color:
E(net)$arrow.size <- .2
E(net)$edge.color <- "blue1"

# We can even set the network layout:
graph_attr(net, "layout") <- layout_as_star(net, center = 'CGE/LGE')
plot(net, edge.curved=.1)

pheatmap(avoidance_mat, cluster_cols = FALSE, cluster_rows = FALSE, cex = 0.8)

clusters = levels(Idents(jy_408))
plots <- lapply(1:length(clusters), function(i){
    plot_clusters_vertical_spatial(jy_408, cluster = clusters[i], pt.size = 0.1)
  })
verts= plot_grid(plotlist = plots, label_size = 1, nrow = 1)
#verts
#ggsave(plot = verts, filename = 'jy_408_spatial_arc_sep.pdf', path = file.path(output_dir_plot, '20221212_1'), width = 7, height = 7, units = 'cm', dpi = 300)
od_idnts = c("MGE", "CGE/LGE", "TBR1+ CGE", "CALB2+DLX2+", "VIP+GAD1+", "SST+LHX6+", "Ex")
pap = plot_clusters_vertical_spatial_no_grid(jy_408, pt.size = 0.1, x_width = 40, force_idents = od_idnts)
#ggsave(plot = pap, filename = 'jy_408_spatial_arc_sep_od.pdf', path = file.path(output_dir_plot, '20221212_1'), width = 7, height = 7, units = 'cm', dpi = 300)
pap

LS0tCnRpdGxlOiAic3Rfbm4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCldyaXR0ZW4gYnkgQXVub3kgUG9kZGFyCkp1bHkgMjFzdCwgMjAyMgoKIyBQcm9jZXNzIHRoZSBwdW5jdGEgcXVhbnRpZmllZCByYXcgZGF0YQpgYGB7ciBldmFsPUZBTFNFfQpjdXJyZW50X2ZpbGUgPC0gcnN0dWRpb2FwaTo6Z2V0QWN0aXZlRG9jdW1lbnRDb250ZXh0KCkkcGF0aApvdXRwdXRfZmlsZSA8LSBzdHJpbmdyOjpzdHJfcmVwbGFjZShjdXJyZW50X2ZpbGUsICcuUm1kJywgJy5SJykKa25pdHI6OnB1cmwoY3VycmVudF9maWxlLCBvdXRwdXQgPSBvdXRwdXRfZmlsZSkKZmlsZS5lZGl0KG91dHB1dF9maWxlKQpgYGAKCiMjIEltcG9ydCBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeSh0aWN0b2MpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkocG5nKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkobWFnaWNrKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShwYWNrY2lyY2xlcykKbGlicmFyeShnZ2FsdCkKbGlicmFyeShjY2NkKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZ2dmb3J0aWZ5KQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGlncmFwaCkKI2xpYnJhcnkoImxlaWRlbiIpCmBgYAoKIyMgTG9hZCB0aGUgZGF0YQpgYGB7cn0KZGF0YV9kaXIgPSAnL2hvbWUvYXVub3kvc3QvYXJjX3Byb2ZpbGluZy9zdF9hbmFseXNpcy9oYW5kX2Fubm90YXRlZF9kYXRhL3JldGhyZXNob2xkZWQnCmNsdW1wX2RpciA9ICcvaG9tZS9hdW5veS9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvY2x1bXBzJwptZXRhX2RpciA9ICcvaG9tZS9hdW5veS9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvb3ZlcmxheScKb3V0cHV0X2Rpcl9wbG90ID0gJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvcmVzdWx0cy9wbG90cycKb3V0cHV0X2Rpcl90YmxzID0gJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvcmVzdWx0cy90YWJsZXMnCmBgYAoKIyMjIHBsb3QgdGhlIG1ldGEgZGF0YSByZWFsIHF1aWNrCmBgYHtyfQptZXRhX250cnNjdHMgPSByZWFkLmNzdihmaWxlLnBhdGgoY2x1bXBfZGlyLCAnbWV0YScsICdNRVRBX250cnNjdC5jc3YnKSwgaGVhZGVyID0gRkFMU0UpICU+JQogIGFzX3RpYmJsZSgpCmBgYAoKIyMjIExvYWQgdGhlIGRhdGEKYGBge3J9CmRmXzQwOCA8LSBsb2FkX3NsaWNlKHNsaWNlID0gIjQwOCIpCmRmXzQwOCA8LSBjbGVhbl80MDgoZGZfNDA4KQpkZl80MDggPC0gZW1iZWRfaG9yaXpvbnRhbF80MDgoZGZfNDA4KQpgYGAKCiMjIFdlIGhhdmUgdGhlIGNvb3JkaW5hdGVzIGZvciA0MDhfVEMgYW5kIG90aGVycwpgYGB7cn0Kcm93bmFtZXMoZGZfNDA4KSA9IDE6bnJvdyhkZl80MDgpCmp5XzQwOCA9IGRmXzQwOCAlPiUKICBkcGx5cjo6c2VsZWN0KC1jKGFyZWEsIElNQUdFLk5BTUUsIFhfaG9yeiwgWV9ob3J6LCBjbHVtcCkpICU+JQogIHQoKSAlPiUKICBDcmVhdGVTZXVyYXRPYmplY3QoKQpqeV80MDgkaW1hZ2UgPC0gZGZfNDA4JElNQUdFLk5BTUUKYGBgCgojIyBqdXN0IHNldCBldmVyeXRoaW5nIGZyb20gYmVsb3cgMSBpbiByYXRpbyB0byB6ZXJvCmBgYHtyfQpqeV80MDggPC0gTm9ybWFsaXplRGF0YShqeV80MDgsIHNjYWxlLmZhY3RvciA9IDFlNSkgIyMjCm5vcm1lZCA9IEdldEFzc2F5RGF0YShqeV80MDgsIHNsb3QgPSAnZGF0YScpCm5vcm1lZFtub3JtZWQgPCAzXSA9IDAKZm9yKGdlbmVfbmFtZSBpbiByb3duYW1lcyhqeV80MDgpKSB7CiAgbWRuX2dlbmVfZXhwciA9IG1lZGlhbihub3JtZWRbZ2VuZV9uYW1lLCBub3JtZWRbZ2VuZV9uYW1lLCBdID4gMF0pCn0KanlfNDA4IDwtIFNldEFzc2F5RGF0YShqeV80MDgsIHNsb3QgPSAnZGF0YScsIG5vcm1lZCkKYGBgCgpgYGB7cn0KanlfNDA4IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGp5XzQwOCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiKQphbGwuZ2VuZXMgPC0gcm93bmFtZXMoanlfNDA4KQpqeV80MDggPC0gU2NhbGVEYXRhKGp5XzQwOCwgZmVhdHVyZXMgPSBhbGwuZ2VuZXMpCmp5XzQwOCA8LSBSdW5QQ0EoanlfNDA4LCBhcHByb3ggPSBGQUxTRSkKanlfNDA4IDwtIEZpbmROZWlnaGJvcnMoanlfNDA4LCBkaW1zID0gMTozMCkKanlfNDA4IDwtIEZpbmRDbHVzdGVycyhqeV80MDgsIHJlc29sdXRpb24gPSAwLjgpCmp5XzQwOCA8LSBSdW5VTUFQKGp5XzQwOCwgZGltcyA9IDE6MzApCgpEaW1QbG90KGp5XzQwOCwgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnc2V1cmF0X2NsdXN0ZXJzJykgKyBOb0F4ZXMoKQpgYGAKYGBge3J9Cmp5XzQwOC5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGp5XzQwOCwgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgbG9nZmMudGhyZXNob2xkID0gMC4yNSkKanlfNDA4Lm1hcmtlcnMgJT4lCiAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogICBzbGljZV9tYXgobiA9IDMyLCBvcmRlcl9ieSA9IGF2Z19sb2cyRkMpCgpgYGAKCmBgYHtyfQpqeV80MDgkY2x1bXAgPSBkZl80MDgkY2x1bXAKCkRpbVBsb3QoanlfNDA4LCAgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdjbHVtcCcpICsgTm9BeGVzKCkgKyBOb0xlZ2VuZCgpCgpgYGAKCgpgYGB7cn0KaGNvb3JkcyA9IGRmXzQwOCAlPiUgZHBseXI6OnNlbGVjdChjKCdYX2hvcnonLCAnWV9ob3J6JykpICU+JSBhcy5tYXRyaXgoKQpjb2xuYW1lcyhoY29vcmRzKSA8LSBjKCdwaXhlbF8xJywgJ3BpeGVsXzInKQoKanlfNDA4W1siSCJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gaGNvb3Jkcywga2V5ID0gInBpeGVsXyIsIGFzc2F5ID0gRGVmYXVsdEFzc2F5KGp5XzQwOCkpCmBgYAoKIyMjIE1lcmdlIGJvdGggZGF0YXNldHMgYW5kIGdlbmVyYXRlIGEgbWV0YWRhdGEgY29sdW1uIHRoYXQgY29ycmVzcG9uZHMKIyMjIHRvIHRoZSBjZWxsICMKYGBge3J9CmRmXzE2NCA8LSBsb2FkX3NsaWNlKHNsaWNlID0gIjE2NCIpCmRmXzE2NCA8LSBjbGVhbl8xNjQoZGZfMTY0KQpkZl8xNjQgPC0gZW1iZWRfaG9yaXpvbnRhbF8xNjQoZGZfMTY0KQpgYGAKCiMjIFdlIGhhdmUgdGhlIGNvb3JkaW5hdGVzIGZvciA0MDhfVEMgYW5kIG90aGVycwpgYGB7cn0Kcm93bmFtZXMoZGZfMTY0KSA9IDE6bnJvdyhkZl8xNjQpCmp5XzE2NCA9IGRmXzE2NCAlPiUKICBkcGx5cjo6c2VsZWN0KC1jKGFyZWEsIElNQUdFLk5BTUUsIFgsIFksIGNsdW1wKSkgJT4lCiAgdCgpICU+JQogIENyZWF0ZVNldXJhdE9iamVjdCgpCmp5XzE2NCRpbWFnZSA9IGRmXzE2NCRJTUFHRS5OQU1FCmBgYAoKIyMganVzdCBzZXQgZXZlcnl0aGluZyBmcm9tIGJlbG93IDEgaW4gcmF0aW8gdG8gemVybwpgYGB7cn0KanlfMTY0IDwtIE5vcm1hbGl6ZURhdGEoanlfMTY0LCBzY2FsZS5mYWN0b3IgPSAxZTUpICMjIwpub3JtZWQgPSBHZXRBc3NheURhdGEoanlfMTY0LCBzbG90ID0gJ2RhdGEnKQpub3JtZWRbbm9ybWVkIDwgM10gPSAwCmZvcihnZW5lX25hbWUgaW4gcm93bmFtZXMoanlfMTY0KSkgewogIG1kbl9nZW5lX2V4cHIgPSBtZWRpYW4obm9ybWVkW2dlbmVfbmFtZSwgbm9ybWVkW2dlbmVfbmFtZSwgXSA+IDBdKQp9Cmp5XzE2NCA8LSBTZXRBc3NheURhdGEoanlfMTY0LCBzbG90ID0gJ2RhdGEnLCBub3JtZWQpCmBgYAoKYGBge3J9Cmp5XzE2NCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhqeV8xNjQsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IikKYWxsLmdlbmVzIDwtIHJvd25hbWVzKGp5XzE2NCkKanlfMTY0IDwtIFNjYWxlRGF0YShqeV8xNjQsIGZlYXR1cmVzID0gYWxsLmdlbmVzKQpqeV8xNjQgPC0gUnVuUENBKGp5XzE2NCwgYXBwcm94ID0gRkFMU0UpCmp5XzE2NCA8LSBGaW5kTmVpZ2hib3JzKGp5XzE2NCwgZGltcyA9IDE6MzApCmp5XzE2NCA8LSBGaW5kQ2x1c3RlcnMoanlfMTY0LCByZXNvbHV0aW9uID0gMC44KQpqeV8xNjQgPC0gUnVuVU1BUChqeV8xNjQsIGRpbXMgPSAxOjMwKQpqeV8xNjQkY2x1bXAgPC0gZGZfMTY0JGNsdW1wCgpEaW1QbG90KGp5XzE2NCwgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnc2V1cmF0X2NsdXN0ZXJzJykgKyBOb0F4ZXMoKQpgYGAKYGBge3J9CkRpbVBsb3QoanlfMTY0LCAgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdjbHVtcCcpICsgTm9BeGVzKCkgKyBOb0xlZ2VuZCgpCmBgYAoKYGBge3J9CnVuaXF1ZShkZl8xNjQkSU1BR0UuTkFNRSkKaW1hZ2VzX29yZGVyZWQgPSBjKCdUQ19Db3J0aWNhbDMnLCAnVENfQ29ydGljYWwyJywgJ1RDX0NvcnRpY2FsMScsICdUQ18xMCcsICdUQ185JywgJ1RDXzgnLCAnVENfNycsICdUQ182JywgJ1RDXzUnLCAnVENfNCcsICdUQ18zJywgJ1RDXzInLCdUQ18xJywnQ0NfMicsJ0NDXzMnLAogICAgICAgICAgICdDQ180JywgJ0NDXzUnLCAnQ0NfNicsICdDQ183JywgJ0NDXzknLCAnQ0NfOCcsICdDQ18xMCcsCiAgICAgICAgICAgJ0NDX0wyLTEnLCAnQ0NfTDItMicsICdDQ19MMi0zJywgJ0NDX0NvcnRpY2FsMScsICdDQ19Db3J0aWNhbDInKQpgYGAKCmBgYHtyfQp4X2hvcnogPSAxOmxlbmd0aChpbWFnZXNfb3JkZXJlZCkgKiAzNQp5X2hvcnogPSByZXAoMCwgbGVuZ3RoKGltYWdlc19vcmRlcmVkKSkKaG9yel9lbWJlZGRpbmcgPSBkYXRhLmZyYW1lKCkKZGZfMTY0JFhfaG9yeiA9IC0xCmRmXzE2NCRZX2hvcnogPSAtMQoKaW1hZ2VzID0gbGlzdC5maWxlcyhtZXRhX2RpcikKZm9yKGkgaW4gMTpsZW5ndGgoaW1hZ2VzX29yZGVyZWQpKXsKICAgIGltYWdlX25hbWUgPSBpbWFnZXNfb3JkZXJlZFtpXQogICAgcHJpbnQoaW1hZ2VfbmFtZSkKICAgIHNwbGl0X25hbWVzID0gc3Ryc3BsaXQoaW1hZ2VfbmFtZSwgJ18nKQogICAgY29ydGV4ID0gdG91cHBlcihzcGxpdF9uYW1lc1tbMV1dWzFdKQogICAgbnVtYmVyID0gc3BsaXRfbmFtZXNbWzFdXVsyXQogICAgbnVtYmVyX2NzdiA9IHBhc3RlMCgnXycsIG51bWJlciwgJy5jc3YnKQogICAgZmlsZW5hbWUgPSBpbWFnZXNbZ3JlcGwoY29ydGV4LCBpbWFnZXMpICYgZ3JlcGwobnVtYmVyX2NzdiwgaW1hZ2VzKSAmIGdyZXBsKCcxNjQnLCBpbWFnZXMpXQogICAgY29vcmRpbmF0ZXMgPSByZWFkLnRhYmxlKGZpbGUucGF0aChtZXRhX2RpciwgZmlsZW5hbWUpLCBzZXAgPSAnLCcsIGhlYWRlciA9IFRSVUUpCiAgICBpZihpbWFnZV9uYW1lID09ICJDQ19MMi0xIil7CiAgICAgIGNvb3JkaW5hdGVzID0gY29vcmRpbmF0ZXNbYygxOjM3LCAzOTpucm93KGNvb3JkaW5hdGVzKSksIF0KICAgIH0KICAgICMjIGNoZWNrZWQgYWxyZWFkeSB0aGF0IGxpc3RzIGFyZSBlcXVhbCwgbWlzc2luZyAxLCAxOCwgMTkgZm9yIG5vdywgbGF5ZXIgMSBhbmQgb3RoZXJzCiAKICAgICMjIHNvIHRoaXMgaXMgYSBsaXR0bGUgdHJpY2t5LCBzbyBuZWVkIHRvIGdldCBpdCByaWdodAogICAgIyMgUmVtZW1iZXIsIGl0IGlzIHRoZSB0b3AgcmlnaHQgdGhhdCB0aGUgY29vcmRpbmF0ZSBpcyBjb21pbmcgZnJvbSwgYnV0CiAgICAjIyB0aGUgYm90dG9tIHJpZ2h0IGlzIHRoZSBuZXcgY29vcmRpbmF0ZSBzcGFjZS4KICAgICMjIHNvIGZpcnN0IHdoZW4gd2UgZ2V0IHRoZSBvcmlnaW5hbCBjb29yZGluYXRlIHNwYWNlLCB0byBzZXQgdG8gcmVsYXRpdmUKICAgICMjIG9mIGJvdHRvbSB3b3VsZCBiZSB0aGUgc2FtZSBYLCBidXQgMTAyNCAtIFkKICAgIAogICAgIyMgcHVzaCBvdXQgdGhlIGNvb3JkaW5hdGVzIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbgogICAgI3hfcmVwZWxsZWQgPC0gKDUxMiAtIGNvb3JkaW5hdGVzJFhfQ29vcmRpbmF0ZV9Jbl9waXhlbHMpCiAgICAKICAgIAogICAgZGZfMTY0W2RmXzE2NCRJTUFHRS5OQU1FID09IGltYWdlX25hbWUsICdYX2hvcnonXSA9IChjb29yZGluYXRlcyRYX0Nvb3JkaW5hdGVfSW5fcGl4ZWxzIC8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElNQUdFX1NJWkUgKiBJTUFHRV9MRU4pICsgeV9ob3J6W2ldCiAgICBkZl8xNjRbZGZfMTY0JElNQUdFLk5BTUUgPT0gaW1hZ2VfbmFtZSwgJ1lfaG9yeiddID0gKCgxMDI0LWNvb3JkaW5hdGVzJFlfQ29vcmRpbmF0ZV9Jbl9waXhlbHMpIC8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElNQUdFX1NJWkUgKiBJTUFHRV9MRU4pICsgeF9ob3J6W2ldCn0KYGBgCgpgYGB7cn0KaGNvb3JkcyA9IGRmXzE2NCAlPiUgZHBseXI6OnNlbGVjdChjKCdYX2hvcnonLCAnWV9ob3J6JykpICU+JSBhcy5tYXRyaXgoKQpjb2xuYW1lcyhoY29vcmRzKSA8LSBjKCdwaXhlbF8xJywgJ3BpeGVsXzInKQoKanlfMTY0W1siSCJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gaGNvb3Jkcywga2V5ID0gInBpeGVsXyIsIGFzc2F5ID0gRGVmYXVsdEFzc2F5KGp5XzE2NCkpCmBgYAoKCmBgYHtyfQpqeV8xNjQ8LSBSZW5hbWVDZWxscyhqeV8xNjQsIGMob3V0ZXIoJzE2NF8nLCAxOm5jb2woanlfMTY0KSwgRlVOPXBhc3RlMCkpKQpqeV8xNjQkYXJlYSA9IGRmXzE2NCRhcmVhCmp5XzQwODwtIFJlbmFtZUNlbGxzKGp5XzQwOCwgYyhvdXRlcignNDA4XycsIDE6bmNvbChqeV80MDgpLCBGVU49cGFzdGUwKSkpCmp5XzQwOCRhcmVhID0gZGZfNDA4JGFyZWEKanlfYWxsIDwtIG1lcmdlKGp5XzE2NCwganlfNDA4KQpgYGAKYGBge3J9Cmp5X2FsbCA8LSBOb3JtYWxpemVEYXRhKGp5X2FsbCwgc2NhbGUuZmFjdG9yID0gMWU1KSAjIyMKbm9ybWVkID0gR2V0QXNzYXlEYXRhKGp5X2FsbCwgc2xvdCA9ICdkYXRhJykKbm9ybWVkW25vcm1lZCA8IDNdID0gMApmb3IoZ2VuZV9uYW1lIGluIHJvd25hbWVzKGp5X2FsbCkpIHsKICBpZiAoZ2VuZV9uYW1lID09ICdEQ1gnKXsKICAgIG1kbl9nZW5lX2V4cHIgPSAwLjUKICAgIHByaW50KCdza2lwIGRjeCcpCiAgfSBlbHNlIGlmICghZ2VuZV9uYW1lICVpbiUgYygnQ09VUFRGMicsICdTUDgnKSl7CiAgICBtZG5fZ2VuZV9leHByID0gbWVkaWFuKG5vcm1lZFtnZW5lX25hbWUsIG5vcm1lZFtnZW5lX25hbWUsIF0gPiAwXSkKICB9ZWxzZXsKICAgIG1kbl9nZW5lX2V4cHIgPSBxdWFudGlsZShub3JtZWRbZ2VuZV9uYW1lLCBub3JtZWRbZ2VuZV9uYW1lLCBdID4gMF0sIC40MCkKICB9CgogIG5vcm1lZFtnZW5lX25hbWUsIG5vcm1lZFtnZW5lX25hbWUsIF0gPCBtZG5fZ2VuZV9leHByXSA9IDAKfQpqeV9hbGwgPC0gU2V0QXNzYXlEYXRhKGp5X2FsbCwgc2xvdCA9ICdkYXRhJywgbm9ybWVkKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MywgZmlnLmhlaWdodCA9IDN9Cmp5X2FsbCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhqeV9hbGwsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IikKYWxsLmdlbmVzIDwtIHJvd25hbWVzKGp5X2FsbCkKanlfYWxsIDwtIFNjYWxlRGF0YShqeV9hbGwsIGZlYXR1cmVzID0gYWxsLmdlbmVzKQpqeV9hbGwgPC0gUnVuUENBKGp5X2FsbCwgYXBwcm94ID0gRkFMU0UpCmp5X2FsbCA8LSBGaW5kTmVpZ2hib3JzKGp5X2FsbCwgZGltcyA9IDE6MzApCmp5X2FsbCA8LSBGaW5kQ2x1c3RlcnMoanlfYWxsLCByZXNvbHV0aW9uID0gMC41KQpqeV9hbGwgPC0gUnVuVU1BUChqeV9hbGwsIGRpbXMgPSAxOjMwKQoKRGltUGxvdChqeV9hbGwsICByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ3NldXJhdF9jbHVzdGVycycpICsgTm9BeGVzKCkKYGBgCgpgYGB7cn0KRGltUGxvdChqeV9hbGwsICByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ2FyZWEnKSArIE5vQXhlcygpICMgTm9MZWdlbmQoKQpgYGAKYGBge3IgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAzfQpGZWF0dXJlUGxvdChqeV9hbGwsIGMoJ0NYQ0wxMicsICdDWENSNCcpLCBjZWxscyA9IHdoaWNoKEZldGNoRGF0YShqeV9hbGwsICdHQUQxJykgPiAwKSwgY29scyA9IGMoICcjRjE4RjAxJywgJyMwNDhCQTgnKSwgYmxlbmQ9IFRSVUUpIApgYGAKCgpgYGB7cn0KbmV3LmNsdXN0ZXIuaWRzID0gYygnQ0dFL0xHRScsCiAgICAgICAgICAgICAgICAgICAgJ0V4JywKICAgICAgICAgICAgICAgICAgICAnVEJSMSsgQ0dFJywKICAgICAgICAgICAgICAgICAgICAnQ0FMQjIrRExYMisnLAogICAgICAgICAgICAgICAgICAgICdWSVArR0FEMSsnLAogICAgICAgICAgICAgICAgICAgICdTU1QrTEhYNisnLAogICAgICAgICAgICAgICAgICAgICdNR0UnKQoKYGBgCgpgYGB7cn0KbmFtZXMobmV3LmNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoanlfYWxsKQpqeV9hbGwgPC0gUmVuYW1lSWRlbnRzKGp5X2FsbCwgbmV3LmNsdXN0ZXIuaWRzKQpgYGAKCmBgYHtyfQojIyBOZWVkIHRvIGV4dHJhY3QgYXMgY2VsbCB4IHBvc2l0aW9uIG1hdHJpeCAoWCwgWSkKIyMgTGV0J3Mgc3RhcnQgd2l0aCA1IG5laWdoYm9ycwpYWV8xNjQgPSBFbWJlZGRpbmdzKGp5XzE2NCwgJ0gnKQpuZWlnaGJvcnMgPSA1Cgpubl9ncmFwaCA9IG5uZyhYWV8xNjQsIGsgPSBuZWlnaGJvcnMpCiNYWV80MDggPSBFbWJlZGRpbmdzKGp5XzQwOCwgJ0gnKQpgYGAKCgpgYGB7cn0KYXZnX3NhbWUgPSBjKCkKYXZnX290aHIgPSBjKCkKZm9yKGkgaW4gMTpuY29sKGp5XzE2NCkpewogICMjIHNvIEkgYW0gZ29pbmcgdG8gaXRlcmF0ZSB0aHJvdWdoIGVhY2ggY2VsbAogICMjIEdldCB0aGUgY2xhc3MgaWRlbnRpdHkgb2YgdGhlIGNlbGwKICBjbGFzc19jZWxsID0gSWRlbnRzKGp5XzE2NClbaV0KICAjIyBHZXQgdGhlIGNlbGxzIHRoYXQgYXJlIHRoZSBzYW1lCiAgbmVhcmVzdF9uZWlnaGJvcnMgPSBhcy5tYXRyaXgobm5fZ3JhcGhbW2ldXSkKICBuZWlnaGJvcl9pZGVudHMgPSBJZGVudHMoanlfMTY0KVtuZWFyZXN0X25laWdoYm9yc1tbMV1dXQogIHNhbWVfaXhzID0gd2hpY2gobmVpZ2hib3JfaWRlbnRzID09IGNsYXNzX2NlbGwpCiAgIyMgR2V0IHRoZSBjZWxscyB0aGF0IGFyZSBub3QgdGhlIHNhbWUKICBvdGhyX2l4cyA9IHdoaWNoKG5laWdoYm9yX2lkZW50cyAhPSBjbGFzc19jZWxsKQogICMjIEdldCB0aGUgYXZyZWFnZSBkaXN0YW5jZSBvZiBzYW1lCiAgc2FtZV9vYmogPSBkaXN0KFhZXzE2NFtjKGksIG5lYXJlc3RfbmVpZ2hib3JzW1sxXV1bc2FtZV9peHNdKSwgXSkKICBhdmdfc2FtZSA9IGMoYXZnX3NhbWUsIG1lYW4oc2FtZV9vYmpbMTpsZW5ndGgoc2FtZV9peHMpXSkpCiAgIyMgR2V0IHRoZSBhdnJlYWdlIGRpc3RhbmNlIG9mIG5vdCBzYW1lCiAgb3Rocl9vYmogPSBkaXN0KFhZXzE2NFtjKGksIG5lYXJlc3RfbmVpZ2hib3JzW1sxXV1bb3Rocl9peHNdKSwgXSkKICBhdmdfb3RociA9IGMoYXZnX290aHIsIG1lYW4oc2FtZV9vYmpbMTpsZW5ndGgob3Rocl9peHMpXSkpCn0KYGBgCgpgYGB7cn0KdGNfY3N2ID0gcmVhZF9jc3YoJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvaGFuZF9hbm5vdGF0ZWRfZGF0YS9jbHVtcC8xNjRfVENfM18xX2Fzc2lnbm1lbnRzLmNzdicsIGNvbF9uYW1lcyA9IEZBTFNFKQpjb2xuYW1lcyh0Y19jc3YpIDwtIGMoJ2NsdW1wJywgJ2FzZ25tJykKYGBgCgpgYGB7cn0KZXhhbXBsZV9saXN0IDwtIHRjX2NzdlsxLCAyXQpgYGAKCmBgYHtyfQpwcmVfc3RyID0gZXhhbXBsZV9saXN0JGFzZ25tCnByZV9zdHIKYGBgCgpgYGB7cn0Kc3RyMSA8LSBnc3ViKCdcXF0nLCAnJywgZ3N1YignXFxbJywgJycsIHByZV9zdHIpKQpzdHIyIDwtIGdzdWIoJ3UnLCAnJywgZ3N1YigiJyIsICcnLCBzdHIxKSkKYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0V9CiMjIExvYWQgZnJvbSAKY2x1bXBzX2RpciA9ICcvaG9tZS9hdW5veS9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvY2x1bXAvY2x1bXBfZGF0YS8nCgpjbHVtcF9udW1fZGYgPSBkYXRhLmZyYW1lKGltZz0gY2hhcmFjdGVyKDApLCB5PSBpbnRlZ2VyKDApLCB6ID0gaW50ZWdlcigwKSkKY29sbmFtZXMoY2x1bXBfbnVtX2RmKSA8LSBjKCdpbWcnLCAncGl4ZWxfZXh0JywgJ2NsdXN0X251bScpCnByZXZfY2x1bXAgPSAndGVtcCcKdGljKCkKCmZvcihmaWxlX25hbWUgaW4gbGlzdC5maWxlcyhjbHVtcHNfZGlyKSl7CiAgIyMjIElmIHRoZSBmaWxlIGlzIGFuIGFzc2lnbm1lbnQgZmlsZSwgc2tpcAogIGlmKGdyZXBsKCdhc3NpZ25tZW50JywgZmlsZV9uYW1lKSl7CiAgICBuZXh0CiAgfQogICMjIyBXZSBuZWVkIHRvIGlzb2xhdGUgdGhlIG5hbWUsIHBpeGVsX2V4dCwgIyBvZiBjbHVzdGVycwogIHNwbGl0X25hbWUgPSBzdHJzcGxpdChmaWxlX25hbWUsICdfJykKICBjbHVtcF9uYW1lID0gcGFzdGUoc3BsaXRfbmFtZVtbMV1dWzE6M10sIGNvbGxhcHNlID0gIl8iKQogIHBpeGVsX2V4dCA9IGFzLmludGVnZXIoc3Ryc3BsaXQoc3BsaXRfbmFtZVtbMV1dWzRdLCAnWy5dJylbWzFdXVsxXSkKICAjIyMgTG9hZCB0aGUgZmlsZQogIGNsdW1wX2NzdiA8LSByZWFkX2NzdihmaWxlLnBhdGgoY2x1bXBzX2RpciwgZmlsZV9uYW1lKSkKICBpZihwcmV2X2NsdW1wICE9IGNsdW1wX25hbWUpewogICAgcHJldl9jbHVtcCA9IGNsdW1wX25hbWUKICAgIHByaW50KGNsdW1wX25hbWUpCiAgfQogIG51bV9jbHVzdGVycyA9IG5yb3coY2x1bXBfY3N2KQogIGNsdW1wX251bV9kZiA8LSBjbHVtcF9udW1fZGYgJT4lCiAgYWRkX3JvdyhpbWcgPSBjbHVtcF9uYW1lLCBwaXhlbF9leHQgPSBwaXhlbF9leHQsIGNsdXN0X251bSA9IG51bV9jbHVzdGVycykKfQp0b2MoKQojIyAxNjQ0IHNlY29uZHMgaXQgdGFla3MsIHNvIGFib3V0IGhhbGYgYW4gaG91ci4gVGhpcyBpcyB3b3J0aCBzYXZpbmcgbG9sCmBgYAoKYGBge3J9CiMjIFNhdmUgdGhpcyBkaWZmaWN1bHRseSBtYWRlIGZpbGUKc2F2ZVJEUyhjbHVtcF9udW1fZGYsIGZpbGUucGF0aCgnL2hvbWUvYXVub3kvc3QvYXJjX3Byb2ZpbGluZy9zdF9hbmFseXNpcy9oYW5kX2Fubm90YXRlZF9kYXRhL2NsdW1wJywgJ2NsdW1wX251bV9kZi5yZHMnKSkKYGBgCgpgYGB7cn0KIyMgR2V0IHRoZSBvcmRlcgpzYW1wIDwtIGNsdW1wX251bV9kZiAlPiUKICBmaWx0ZXIocGl4ZWxfZXh0ID09IDEpICU+JQogIGFycmFuZ2UoY2x1c3RfbnVtKQoKb3JkZXIgPSBzYW1wJGltZwpgYGAKCgpgYGB7ciBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gM30KY2x1bXBfbnVtX2RmICU+JQogICNmaWx0ZXIocGl4ZWxfZXh0IDw9IDEwKSAlPiUKICBhcnJhbmdlKGNsdXN0X251bSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gaW1nLCB5ID0gY2x1c3RfbnVtLCBncm91cCA9IGZhY3RvcihwaXhlbF9leHQpKSkgKyAKICBnZW9tX2xpbmUoYWVzKGNvbG9yPWZhY3RvcihwaXhlbF9leHQpKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1mYWN0b3IocGl4ZWxfZXh0KSkpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBvcmRlcikgKyB0aGVtZV9jbGFzc2ljKCkgKyBSb3RhdGVkQXhpcygpCmBgYAoKYGBge3J9CgpnZXRfbnVtZXJpY19hc3NpZ25tZW50cyA8LSBmdW5jdGlvbihhc3NpZ25tZW50X3N0cil7CiAgc3RyMSA8LSBnc3ViKCdcXF0nLCAnJywgZ3N1YignXFxbJywgJycsIGFzc2lnbm1lbnRfc3RyKSkKICBzdHIyIDwtIGdzdWIoJ3UnLCAnJywgZ3N1YigiJyIsICcnLCBzdHIxKSkKICByZXR1cm4oYXMubnVtZXJpYyh1bmxpc3Qoc3Ryc3BsaXQoc3RyMiwgJywnKSkpKQp9CgpnZXRfYXNzaWdubWVudHMgPC0gZnVuY3Rpb24oYXNzaWdubWVudF9zdHIsIGltYWdlX25hbWUpewogICMjIyBicmVha2Rvd24KICBhc3NpZ25tZW50X2xpc3QgPC0gZ2V0X251bWVyaWNfYXNzaWdubWVudHMoYXNzaWdubWVudF9zdHIpCiAgaWYobGVuZ3RoKGFzc2lnbm1lbnRfbGlzdCkgPT0gMCl7CiAgICByZXR1cm4oLTEpCiAgfQogIGlmKGdyZXBsKCcxNjQnLCBpbWFnZV9uYW1lKSl7CiAgICBzZGYgPC0gZGZfMTY0CiAgICBzb2JqIDwtIGp5XzE2NAogIH0KICBlbHNlewogICAgc2RmIDwtIGRmXzQwOAogICAgc29iaiA8LSBqeV80MDgKICB9CiAgaW1nX25hbWVfYWJiciA9IGdzdWIoJy4uLl8nLCAnJywgaW1hZ2VfbmFtZSkKICBjZWxsX2l4cyA8LSB3aGljaChzZGYkSU1BR0UuTkFNRSA9PSBpbWdfbmFtZV9hYmJyKQogIGltYWdlX3NvYmogPC0gc29ialssIGNlbGxfaXhzXSAKICBjZWxsX25hbWVzIDwtIGNvbG5hbWVzKGltYWdlX3NvYmpbLCBhc3NpZ25tZW50X2xpc3RdKQogICMjIGp1c3QgcmV0dXJuIHRoZSBuYW1lcwogIGNlbGxzIDwtIGp5X2FsbFssIHdoaWNoKGNvbG5hbWVzKGp5X2FsbCkgJWluJSBjZWxsX25hbWVzKV0KICAjIyBXaWxsIHdlIGdldCB0aGUgc2FtZSByZXNwb25zZSBlYWNoIHRpbWU/CiAgcmV0dXJuKGNlbGxzKQp9CmBgYAoKCgpgYGB7ciBtZXNzYWdlID0gRkFMU0V9CiMjIyBTbyBJIGFtIGdvaW5nIHRvIG1ha2UgYSBjbHVtcCBERiBhbmQgYWxzbyBnb2luZyB0byB1cGRhdGUganlfYWxsCiMjIExvYWQgZnJvbSAKY2x1bXBzX2RpciA9ICcvaG9tZS9hdW5veS9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvY2x1bXAvY2x1bXBfZGF0YS8nCnBpeGVsX2V4dCA9ICcxJwoKCmZlYXR1cmVzID0gYygiLi4uMSIsIkFyZWEiLCAiUGVyaW0uIiwgIk1ham9yIiwgIk1pbm9yIiwgIkFuZ2xlIiwKICAgICAgICAgICJDaXJjLiIsICJGZXJldCIsICIlQXJlYSIsIk1pbkZlcmV0IiwgIkFSIiwKICAgICAgICAgICJSb3VuZCIsICJTb2xpZGl0eSIgICkKCiNqeV9hbGwkY2x1bXAgPSAiTmFOIgoKY2x1bXBfZGYgPSBkYXRhLmZyYW1lKCkKdGljKCkKI1s1MDE6bGVuZ3RoKGxpc3QuZmlsZXMoY2x1bXBzX2RpcikpXQpmb3IoZmlsZV9uYW1lIGluIGxpc3QuZmlsZXMoY2x1bXBzX2RpcikpewogICMjIyBJZiB0aGUgZmlsZSBpcyBhbiBhc3NpZ25tZW50IGZpbGUsIHNraXAKICBpZighZ3JlcGwocGFzdGUwKCdfJywgcGl4ZWxfZXh0LCAnLmNzdicpLCBmaWxlX25hbWUpIHx8IGdyZXBsKCdhc3NpZ25tZW50cycsIGZpbGVfbmFtZSkpeyAjfHwgZ3JlcGwoJzE2NCcsIGZpbGVfbmFtZSkpewogICAgbmV4dAogIH0KICBwcmludChmaWxlX25hbWUpCiAgIyMjIFdlIG5lZWQgdG8gaXNvbGF0ZSB0aGUgbmFtZSwgcGl4ZWxfZXh0LCAjIG9mIGNsdXN0ZXJzCiAgc3BsaXRfbmFtZSA9IHN0cnNwbGl0KGZpbGVfbmFtZSwgJ18nKQogIGNsdW1wX25hbWUgPSBwYXN0ZShzcGxpdF9uYW1lW1sxXV1bMTozXSwgY29sbGFwc2UgPSAiXyIpCiAgYXNzaWdubWVudF9uYW1lID0gcGFzdGUwKGNsdW1wX25hbWUsICdfJywgcGl4ZWxfZXh0LCAnX2Fzc2lnbm1lbnRzLmNzdicpCiAgI3BpeGVsX2V4dCA9IGFzLmludGVnZXIoc3Ryc3BsaXQoc3BsaXRfbmFtZVtbMV1dWzRdLCAnWy5dJylbWzFdXVsxXSkKICAjIyMgTG9hZCB0aGUgZmlsZQogIGNsdW1wX25hbWUgPSBnc3ViKCdDb3J0aWNhbF8nLCAnQ29ydGljYWwnLCBjbHVtcF9uYW1lKQogIGNsdW1wX25hbWUgPSBnc3ViKCdMMl8nLCAnTDItJywgY2x1bXBfbmFtZSkKICBjbHVtcF9jc3YgPC0gcmVhZF9jc3YoZmlsZS5wYXRoKGNsdW1wc19kaXIsIGZpbGVfbmFtZSkpCiAgY2x1bXBfY3N2JC4uLjEgPC0gYXMuY2hhcmFjdGVyKGNsdW1wX2NzdiQuLi4xKQogIGFzc2lnbm1lbnRzX2NzdiA8LSByZWFkX2NzdihmaWxlLnBhdGgoY2x1bXBzX2RpciwgYXNzaWdubWVudF9uYW1lKSwgY29sX25hbWVzID0gRkFMU0UpCiAgCiAgdGVtcCA8LSBkYXRhLmZyYW1lKCkKICBndGVtcCA8LSBkYXRhLmZyYW1lKCkKICBjdHlwZV9uYW1lcyA8LSBsZXZlbHMoSWRlbnRzKGp5X2FsbCkpCiAgZm9yKGkgaW4gMTpucm93KGNsdW1wX2NzdikpewogICAgaXRyX25hbWUgPSBwYXN0ZTAoY2x1bXBfbmFtZSwgJ19DbHVtcF8nLCBpKQogICAgY2x1bXBfY3N2W2ksIDFdID0gaXRyX25hbWUKICAgIGFzc2lnbm1lbnRfc3RyID0gYXNzaWdubWVudHNfY3N2W3doaWNoKGFzc2lnbm1lbnRzX2NzdiRYMSA9PSBpdHJfbmFtZSksIDJdW1sxXV0KICAgIGNlbGxzID0gZ2V0X2Fzc2lnbm1lbnRzKGFzc2lnbm1lbnRfc3RyLCBjbHVtcF9uYW1lKQogICAgY3R5cGVzIDwtIG1hdHJpeCgwLCAxLCBsZW5ndGgodW5pcXVlKElkZW50cyhqeV9hbGwpKSkpCiAgICBjbHVtcF9leHByIDwtIG1hdHJpeChOYU4sIDEsIGxlbmd0aChyb3duYW1lcyhqeV9hbGwpKSkKICAgIGlmKGNsYXNzKGNlbGxzKSAhPSAibnVtZXJpYyIpewogICAgICBmb3IoaiBpbiAxOm5jb2woY2VsbHMpKSB7CiAgICAgICAgY3R5cGUgPSB3aGljaChjdHlwZV9uYW1lcyA9PSBJZGVudHMoY2VsbHMpW1tqXV0pCiAgICAgICAgY3R5cGVzW2N0eXBlXSA9IGN0eXBlc1tjdHlwZV0gKyAxCiAgICAgIH0KICAgICAgY2x1bXBfZXhwciA8LSByb3dNZWFucyhhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNlbGxzLCAnZGF0YScpKSkKICAgICAganlfYWxsJGNsdW1wW3doaWNoKGNvbG5hbWVzKGp5X2FsbCkgJWluJSBjb2xuYW1lcyhjZWxscykpXSA9IGNsdW1wX25hbWUKICAgIH0gCiAgZWxzZXsKICAgICAgY29sbmFtZXMoY2x1bXBfZXhwcikgPC0gY29sbmFtZXMoZ3RlbXApCiAgICB9CiAgICB0ZW1wIDwtIHJiaW5kKHRlbXAsIGN0eXBlcykKICAgICMjIyBUaGlzIGlzIHRvIGdldCBhdmVyYWdlIGV4cHJlc3Npb24gaW4gY2x1bXAKICAgIGd0ZW1wIDwtIHJiaW5kKGd0ZW1wLCBjbHVtcF9leHByKQogIH0KICBjb2xuYW1lcyh0ZW1wKSA8LSBjdHlwZV9uYW1lcwogIGNvbG5hbWVzKGd0ZW1wKSA8LSByb3duYW1lcyhqeV9hbGwpCiAgY2x1bXBfY3N2X3RvX2FkZCA8LSBjbHVtcF9jc3YgJT4lIGRwbHlyOjpzZWxlY3QoZmVhdHVyZXMpCiAgY2x1bXBfZGYgPC0gcmJpbmQoY2x1bXBfZGYsIGNiaW5kKGNsdW1wX2Nzdl90b19hZGQsIHRlbXAsIGd0ZW1wKSkKICAjcHJpbnQoYXNzaWdubWVudF9uYW1lKQogICNkZl80MDggPC0gcmJpbmQoZGZfNDA4LCBkZl90b19hcHBlbmQpCiAgIyMjIEdvdHRhIG1ha2Ugc3VyZSB0aGF0IHRoZSBpbWFnZSBuYW1lcyBhcmUgdGhlIHNhbWUsIHRoZXJlIGFyZSBzb21lIGRpc2NyZXBhbmNpZXMKfQojIyBTYXZlIHRoaXMgZGlmZmljdWx0bHkgbWFkZSBmaWxlCnNhdmVSRFMoY2x1bXBfZGYsIGZpbGUucGF0aCgnL2hvbWUvYXVub3kvc3QvYXJjX3Byb2ZpbGluZy9zdF9hbmFseXNpcy9oYW5kX2Fubm90YXRlZF9kYXRhL2NsdW1wJywgJ2NsdW1wX2RmLnJkcycpKQp0b2MoKQpgYGAKCmBgYHtyfQpjbHVtcF9kZl80MDggPC0gcmVhZFJEUyhmaWxlLnBhdGgoJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvaGFuZF9hbm5vdGF0ZWRfZGF0YS9jbHVtcCcsICdjbHVtcF9kZl80MDgucmRzJykpCmBgYAoKYGBge3J9CnRlbXBvcmFyeQpgYGAKYGBge3J9CmNsdW1wX2RmIDwtIHJiaW5kKGNsdW1wX2RmLCBjbHVtcF9kZl80MDgpCmBgYAoKYGBge3J9CnNhdmVSRFMoY2x1bXBfZGYsIGZpbGUucGF0aCgnL2hvbWUvYXVub3kvc3QvYXJjX3Byb2ZpbGluZy9zdF9hbmFseXNpcy9oYW5kX2Fubm90YXRlZF9kYXRhL2NsdW1wJywgJ2NsdW1wX2RmLnJkcycpKQpgYGAKCmBgYHtyfQpjbHVtcF9kZl8xNjQgPC0gcmVhZFJEUyhmaWxlLnBhdGgoJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvaGFuZF9hbm5vdGF0ZWRfZGF0YS9jbHVtcCcsICdjbHVtcF9kZl8xNjQucmRzJykpCmBgYApgYGB7cn0KY2x1bXBfZGYgPC0gcmJpbmQoY2x1bXBfZGZfMTY0LCBjbHVtcF9kZikKYGBgCgoKIyBTVEFSVCBIRVJFCmBgYHtyfQpjbHVtcF9kZiA8LSByZWFkUkRTKGZpbGUucGF0aCgnL2hvbWUvYXVub3kvc3QvYXJjX3Byb2ZpbGluZy9zdF9hbmFseXNpcy9oYW5kX2Fubm90YXRlZF9kYXRhL2NsdW1wJywgJ2NsdW1wX2RmLnJkcycpKQpgYGAKCgoKYGBge3J9CmNsdW1wX2RmICU+JQogIGdncGxvdChhZXMoeCA9IEFyZWEsIHkgPSBQZXJpbS4pKSArIGdlb21fcG9pbnQoKQpgYGAKYGBge3J9CmNsdW1wX3BjYSA8LSBwcmNvbXAoY2x1bXBfZGZbLCAxNDoyMF0sIGNlbnRlciA9IFRSVUUsc2NhbGUuID0gVFJVRSkKCnN1bW1hcnkoY2x1bXBfcGNhKQoKYGBgCgpgYGB7cn0KbGlicmFyeShnZ2ZvcnRpZnkpCmF1dG9wbG90KGNsdW1wX3BjYSkKYGBgCmBgYHtyfQphdXRvcGxvdChjbHVtcF9wY2EsIGRhdGEgPSBjbHVtcF9kZiwgY29sb3VyID0gJ3N0cmVhbScpCmBgYApgYGB7cn0KY2x1bXBfZGYkc3RyZWFtID0gIndyb25nIgpjbHVtcF9kZiRzdHJlYW1bd2hpY2goZ3JlcGwoJzQwOF9UQycsIGNsdW1wX2RmJC4uLjEpKV0gPSAiNDA4X1RDIgpjbHVtcF9kZiRzdHJlYW1bd2hpY2goZ3JlcGwoJzE2NF9UQycsIGNsdW1wX2RmJC4uLjEpKV0gPSAiMTY0X1RDIgpjbHVtcF9kZiRzdHJlYW1bd2hpY2goZ3JlcGwoJzQwOF9DQycsIGNsdW1wX2RmJC4uLjEpKV0gPSAiNDA4X0NDIgpjbHVtcF9kZiRzdHJlYW1bd2hpY2goZ3JlcGwoJzE2NF9DQycsIGNsdW1wX2RmJC4uLjEpKV0gPSAiMTY0X0NDIgpjbHVtcF9kZiRzdHJlYW1bd2hpY2goZ3JlcGwoJzQwOF9Db3J0aWNhbCcsIGNsdW1wX2RmJC4uLjEpKV0gPSAiUENvcnQiCmNsdW1wX2RmJHN0cmVhbVt3aGljaChncmVwbCgnMTY0X1RDX0NvcnRpY2FsJywgY2x1bXBfZGYkLi4uMSkpXSA9ICJBVENvcnQiCmNsdW1wX2RmJHN0cmVhbVt3aGljaChncmVwbCgnMTY0X0NDX0NvcnRpY2FsJywgY2x1bXBfZGYkLi4uMSkpXSA9ICJBQ0NvcnQiCmNsdW1wX2RmJHN0cmVhbVt3aGljaChncmVwbCgnNDA4X0wyJywgY2x1bXBfZGYkLi4uMSkpXSA9ICJQTDIiCmNsdW1wX2RmJHN0cmVhbVt3aGljaChncmVwbCgnMTY0X0NDX0wyJywgY2x1bXBfZGYkLi4uMSkpXSA9ICJBTDIiCmBgYAoKYGBge3J9CmNsdW1wX2RmICU+JQogIGdncGxvdChhZXMoeCA9IHN0cmVhbSwgeSA9IEFyZWEpKSArIGdlb21fdmlvbGluKCkKYGBgCgojIyBIb3cgbWFueSBjbHVtcHMgYXJlIHRoZXJlPwojIyBIb3cgYmlnIGFyZSB0aGVzZSBjbHVtcHM/CiMjIFdoYXQgcGVyY2VudCBvZiBEQ1ggcG9zaXRpdmUgY2VsbHMgYXJlIGluIGNsdW1wcz8KCmBgYHtyfQphc3VtbWFyeSgpCmBgYAoKYGBge3J9CmNsdW1wX2RmICU+JSBncm91cF9ieShzdHJlYW0pICU+JQpzdW1tYXJpemUoKQpgYGAKCmBgYHtyfQpjbHVtcF9kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBzdHJlYW0sIHkgPSBBcmVhKSkgKyBnZW9tX3Zpb2xpbigpCmBgYAoKYGBge3J9CmNsdW1wX2RmJGltZyA8LSBzdHJfc3BsaXRfZml4ZWQoY2x1bXBfZGYkLi4uMSwgIl9DbHVtcCIsIDIpWywgMV0KYGBgCgoKYGBge3IgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0KY2x1bXBfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3RyZWFtLCB5ID0gQXJlYSwgY29sb3IgPSAnVEJSMSsgQ0dFJykpICsgZ2VvbV9wb2ludChzaXplID0gY2x1bXBfZGYkQXJlYSAvIDEwZTQsICkgKyBSb3RhdGVkQXhpcygpCmBgYAoKYGBge3J9CgpzdWJzZXRfZGYgPC0gY2x1bXBfZGYgJT4lCiAgZmlsdGVyKGdyZXBsKCdfJywgc3RyZWFtKSkKCmNsdW1wX2RmICU+JQogIGZpbHRlcihncmVwbCgnXycsIHN0cmVhbSkpICU+JQogIGdncGxvdChhZXMoeCA9IHN0cmVhbSwgeSA9IG5jZWxsKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21fcG9pbnQoc2l6ZSA9IHN1YnNldF9kZiRBcmVhIC8gMTBlNCwgKSArIFJvdGF0ZWRBeGlzKCkgKyB5bGFiKCcjIERDWCBjZWxscyBpbiBjbHVtcCcpCmBgYApgYGB7cn0KY2x1bXBfZGYgJT4lCiAgZmlsdGVyKGdyZXBsKCdfJywgc3RyZWFtKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3RyZWFtLCB5ID0gQXJlYSkpICsgZ2VvbV9ib3hwbG90KCkgKyAgUm90YXRlZEF4aXMoKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHVtYXApCmNsdW1wX3VtYXAgPC0gdW1hcChjbHVtcF9wY2EkeCkKYGBgCmBgYHtyfQp1bWFwX2NkcyA8LSBhcy5kYXRhLmZyYW1lKGNsdW1wX3VtYXAkbGF5b3V0KQpjb2xuYW1lcyh1bWFwX2NkcykgPC0gYygndW1hcDEnLCAndW1hcDInKQoKY2x1bXBfZGYkdW1hcDEgPC0gdW1hcF9jZHMkdW1hcDEKY2x1bXBfZGYkdW1hcDIgPC0gdW1hcF9jZHMkdW1hcDIKYGBgCgpgYGB7cn0KY2x1bXBfZGYgJT4lCiAgZmlsdGVyKHVtYXAyIDwgMTApICU+JQogIGFycmFuZ2UoQ0FMQjJfcGN0KSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB1bWFwMSwgeSA9IHVtYXAyLCBjb2xvciA9IENBTEIyX3BjdCkpICsgZ2VvbV9wb2ludChzaXplID0gMC44LCBhbHBoYSA9IDAuOCkKYGBgCmBgYHtyfQpjbHVtcF9kZiAlPiUKICAjZmlsdGVyKGdyZXBsKCdfJywgc3RyZWFtKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdW1hcDEsIHkgPSB1bWFwMiwgY29sb3IgPSBzdHJlYW0pKSArIGdlb21fcG9pbnQoc2l6ZSA9IDAuOCwgYWxwaGEgPSAwLjUpCmBgYAoKYGBge3J9CmNsdW1wX2RmJG5jZWxsIDwtIHJvd1N1bXMoY2x1bXBfZGZbLCAxNDoyMF0pCmBgYAoKYGBge3J9CmNsdW1wX2RmJE1HRV9wY3QgPC0gY2x1bXBfZGYkTUdFIC8gY2x1bXBfZGYkbmNlbGwKY2x1bXBfZGYkVEJSMV9wY3QgPC0gY2x1bXBfZGYkYFRCUjErIENHRWAgLyBjbHVtcF9kZiRuY2VsbApjbHVtcF9kZiRFeF9wY3QgPC0gY2x1bXBfZGYkRXggLyBjbHVtcF9kZiRuY2VsbApjbHVtcF9kZiRTU1RfcGN0IDwtIGNsdW1wX2RmJGBTU1QrTEhYNitgIC8gY2x1bXBfZGYkbmNlbGwKY2x1bXBfZGYkQ0FMQjJfcGN0IDwtIGNsdW1wX2RmJGBDQUxCMitETFgyK2AgLyBjbHVtcF9kZiRuY2VsbApjbHVtcF9kZiRWSVBfcGN0IDwtIGNsdW1wX2RmJGBWSVArR0FEMStgIC8gY2x1bXBfZGYkbmNlbGwKY2x1bXBfZGYkQ0dFX3BjdCA8LSBjbHVtcF9kZiRgQ0dFL0xHRWAgLyBjbHVtcF9kZiRuY2VsbApgYGAKCmBgYHtyfQp3ZWlyZF9jbHVzdGVyIDwtIGNsdW1wX2RmICU+JQogIGZpbHRlcih1bWFwMiA+IDEwKQpgYGAKYGBge3J9CndlaXJkX2NsdXN0ZXIKYGBgCmBgYHtyfQpjbHVtcF9kZiRNR0VfcGN0IDwtIGNsdW1wX2RmJE1HRSAvIGNsdW1wX2RmJG5jZWxsCmNsdW1wX2RmJFRCUjFfcGN0IDwtIGNsdW1wX2RmJGBUQlIxKyBDR0VgIC8gY2x1bXBfZGYkbmNlbGwKY2x1bXBfZGYkRXhfcGN0IDwtIGNsdW1wX2RmJEV4IC8gY2x1bXBfZGYkbmNlbGwKY2x1bXBfZGYkU1NUX3BjdCA8LSBjbHVtcF9kZiRgU1NUK0xIWDYrYCAvIGNsdW1wX2RmJG5jZWxsCmNsdW1wX2RmJENBTEIyX3BjdCA8LSBjbHVtcF9kZiRgQ0FMQjIrRExYMitgIC8gY2x1bXBfZGYkbmNlbGwKY2x1bXBfZGYkVklQX3BjdCA8LSBjbHVtcF9kZiRgVklQK0dBRDErYCAvIGNsdW1wX2RmJG5jZWxsCmNsdW1wX2RmJENHRV9wY3QgPC0gY2x1bXBfZGYkYENHRS9MR0VgIC8gY2x1bXBfZGYkbmNlbGwKYGBgCgpgYGB7cn0KI2NsdW1wX2RmJG9yZGVyID0gaGNsdXN0X2F2ZyRvcmRlcgpicGxvdF9kZiA8LSBjbHVtcF9kZltoY2x1c3RfYXZnJG9yZGVyLCBjKDEsIDIxLDI1LCAyNjozMildICU+JQogIGZpbHRlcihNR0VfcGN0ICE9ICdOYU4nKSAlPiUKICBwaXZvdF9sb25nZXIoIWMoLi4uMSwgc3RyZWFtLCBuY2VsbCksIG5hbWVzX3RvID0gInBjdF90eXBlIiwgdmFsdWVzX3RvID0gInBjdCIpCmBgYAoKYGBge3IgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSAzfQpicGxvdF9kZiAgJT4lCiAgZmlsdGVyKG5jZWxsID4gNSkgJT4lCiAgI2ZpbHRlcihncmVwbCgnMTY0Jywgc3RyZWFtKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gLi4uMSwgeSA9IHBjdCwgZmlsbCA9IHBjdF90eXBlKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uPSJzdGFjayIsIHN0YXQ9ImlkZW50aXR5IikgICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpICsgeGxhYignY2x1bXAnKSAjcmVtb3ZlIHggYXhpcyBsYWJlbHMpICsgeGwKYGBgCgpgYGB7cn0KZGlzdF9tYXQgPC0gZGlzdChjbHVtcF9wY2EkeCwgbWV0aG9kID0gJ2V1Y2xpZGVhbicpCmhjbHVzdF9hdmcgPC0gaGNsdXN0KGRpc3RfbWF0LCBtZXRob2QgPSAnYXZlcmFnZScpCnBsb3QoaGNsdXN0X2F2ZykKYGBgCmBgYHtyfQphdmdfc2FtZSA9IGMoKQphdmdfb3RociA9IGMoKQpmb3IoaSBpbiAxOm5jb2woY2x1bXBfZGYpKXsKICAjIyBzbyBJIGFtIGdvaW5nIHRvIGl0ZXJhdGUgdGhyb3VnaCBlYWNoIGNlbGwKICAjIyBHZXQgdGhlIGNsYXNzIGlkZW50aXR5IG9mIHRoZSBjZWxsCiAgY2xhc3NfY2VsbCA9IElkZW50cyhqeV8xNjQpW2ldCiAgIyMgR2V0IHRoZSBjZWxscyB0aGF0IGFyZSB0aGUgc2FtZQogIG5lYXJlc3RfbmVpZ2hib3JzID0gYXMubWF0cml4KG5uX2dyYXBoW1tpXV0pCiAgbmVpZ2hib3JfaWRlbnRzID0gSWRlbnRzKGp5XzE2NClbbmVhcmVzdF9uZWlnaGJvcnNbWzFdXV0KICBzYW1lX2l4cyA9IHdoaWNoKG5laWdoYm9yX2lkZW50cyA9PSBjbGFzc19jZWxsKQogICMjIEdldCB0aGUgY2VsbHMgdGhhdCBhcmUgbm90IHRoZSBzYW1lCiAgb3Rocl9peHMgPSB3aGljaChuZWlnaGJvcl9pZGVudHMgIT0gY2xhc3NfY2VsbCkKICAjIyBHZXQgdGhlIGF2cmVhZ2UgZGlzdGFuY2Ugb2Ygc2FtZQogIHNhbWVfb2JqID0gZGlzdChYWV8xNjRbYyhpLCBuZWFyZXN0X25laWdoYm9yc1tbMV1dW3NhbWVfaXhzXSksIF0pCiAgYXZnX3NhbWUgPSBjKGF2Z19zYW1lLCBtZWFuKHNhbWVfb2JqWzE6bGVuZ3RoKHNhbWVfaXhzKV0pKQogICMjIEdldCB0aGUgYXZyZWFnZSBkaXN0YW5jZSBvZiBub3Qgc2FtZQogIG90aHJfb2JqID0gZGlzdChYWV8xNjRbYyhpLCBuZWFyZXN0X25laWdoYm9yc1tbMV1dW290aHJfaXhzXSksIF0pCiAgYXZnX290aHIgPSBjKGF2Z19vdGhyLCBtZWFuKHNhbWVfb2JqWzE6bGVuZ3RoKG90aHJfaXhzKV0pKQp9CmBgYAoKYGBge3J9CmNsdW1wX2RmJG1heF9wY3QgPC0gY2x1bXBfZGZbLCAyNjozMl0gJT4lCiAgYXBwbHkoMSwgbWF4KQpgYGAKCmBgYHtyfQpjbHVtcF9kZiRobWdfaXggPC0gKGNsdW1wX2RmJG1heF9wY3QgLSAoMS83KSkgLyAoMS0xLzcpCmBgYAoKYGBge3J9CmNsdW1wX2RmICU+JQogIGZpbHRlcighaXMubmEoaG1nX2l4KSkgJT4lCiAgZmlsdGVyKG5jZWxsID4gMykgJT4lCmdncGxvdChhZXMoeD1obWdfaXgpKSArIAogIGdlb21faGlzdG9ncmFtKGNvbG9yPSJibGFjayIsIGZpbGw9IndoaXRlIikKYGBgCgpgYGB7cn0KY2x1bXBfMyA8LSBjbHVtcF9kZiAlPiUgZmlsdGVyKG5jZWxsPjMpCm5jdHlwID0gNwpidHN0cnAgPSAxMDAwCgpudWxsX2Rpc3QgPSBtYXRyaXgoMCwgbnJvdyA9IGJ0c3RycCwgbmNvbCA9IG5yb3coY2x1bXBfMykpCnRpYygpCmZvcihrIGluIDE6YnRzdHJwKXsKICBpZihrICUlIDI1MCA9PSAwKXsKICAgIHByaW50KGspCiAgfQogIGZvcihpIGluIDE6bnJvdyhjbHVtcF8zKSl7CiAgICAgY2VsbHMgPSBjbHVtcF8zJG5jZWxsW2ldCiAgICAgc2FtcCA8LSByZHVuaWYoY2VsbHMsIDEgLCA3KQogICAgIG14X3BjdCA8LSBtYXgodGFibGUoc2FtcCkpIC8gY2VsbHMKICAgICBudWxsX2Rpc3RbaywgaV0gPSAobXhfcGN0IC0gKDEvNykpIC8gKDEtMS83KQogIH0KfQp0b2MoKQpgYGAKYGBge3J9Cm51bGxfZGlzdApgYGAKCmBgYHtyfQpoaXN0KG51bGxfZGlzdCkKYGBgCmBgYHtyfQpudWxsX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMudmVjdG9yKGNvbE1lYW5zKG51bGxfZGlzdCkpKQpjb2xuYW1lcyhudWxsX2RmKSA8LSAnaXhzJwpudWxsX2RmJGRpc3QgPSAnbnVsbCcKbnVsbF9kZgpgYGAKCmBgYHtyfQp0cnVlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoY2x1bXBfMyRobWdfaXgsICd0cnVlJykpCmNvbG5hbWVzKHRydWVfZGYpIDwtIGMoJ2l4cycsICdkaXN0JykKbWdfZGYgPC0gcmJpbmQobnVsbF9kZiwgdHJ1ZV9kZikKbWdfZGYkaXhzIDwtIGFzLm51bWVyaWMobWdfZGYkaXhzKQpgYGAKCmBgYHtyfQptZ19kZiAlPiUKICBnZ3Bsb3QoYWVzKHg9aXhzLCBjb2xvcj1kaXN0KSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9IndoaXRlIiwgIGFscGhhPTAuNCwgcG9zaXRpb249ImlkZW50aXR5IikKYGBgCgpgYGB7cn0KanlfYWxsJGNsdW1wCmBgYAoKYGBge3J9Cmp5X2FsbCRpbl9jbHVtcCA8LSAnTm90IGluIENsdW1wJwpqeV9hbGwkaW5fY2x1bXBbd2hpY2goanlfYWxsJGNsdW1wICE9ICJOYU4iKV0gPC0gJ0luIENsdW1wJwpgYGAKCmBgYHtyfQpEb3RQbG90KGp5X2FsbCwgZmVhdHVyZXMgPSBjKCdDWENSNCcsICdDWENSNycsICdFR0ZSJywgJ1ZMRExSJywgJ1JFTE4nLCAnTFJQOCcpLCBncm91cC5ieSA9ICdpbl9jbHVtcCcpICsgUm90YXRlZEF4aXMoKQpgYGAKCmBgYHtyfQpqeV9hbGwkY2x1bXBfc3RyZWFtIDwtIHBhc3RlKGp5X2FsbCRhcmVhLCBqeV9hbGwkaW5fY2x1bXApCmBgYAoKYGBge3J9CkRvdFBsb3QoanlfYWxsLCBmZWF0dXJlcyA9IGMoJ0NYQ1I0JywgJ0NYQ1I3JywgJ0VHRlInLCAnVkxETFInLCAnUkVMTicsICdMUlA4JyksIAogICAgICAgIGdyb3VwLmJ5ID0gJ2NsdW1wX3N0cmVhbScpICsgUm90YXRlZEF4aXMoKQpgYGAKYGBge3IgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDE0fQpEb3RQbG90KGp5X2FsbCwgZmVhdHVyZXMgPSByb3duYW1lcyhqeV9hbGwpLCBncm91cC5ieSA9ICdjbHVtcF9zdHJlYW0nKSArIFJvdGF0ZWRBeGlzKCkKYGBgCmBgYHtyIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpEaW1QbG90KGp5X2FsbCwgZ3JvdXAuYnkgPSAnaW5fY2x1bXAnLCBzcGxpdC5ieSA9ICdhcmVhJywgbmNvbCA9IDUpCmBgYAoKYGBge3J9Cmp5X2FsbCRhdnAgPSAnd3JvbmcnCmp5X2FsbCRhdnBbd2hpY2goZ3JlcGwoJzE2NCcsIGp5X2FsbCRhcmVhKSldID0gJzE2NCcKanlfYWxsJGF2cFt3aGljaChncmVwbCgnNDA4JywganlfYWxsJGFyZWEpKV0gPSAnNDA4JwpgYGAKYGBge3J9Cmp5X2FsbCRjbHVtcF9zdHJlYW0yIDwtIHBhc3RlKGp5X2FsbCRhdnAsIGp5X2FsbCRpbl9jbHVtcCkKYGBgCgpgYGB7cn0KanlfYWxsJGNsdW1wX3Bsb3QgPSBqeV9hbGwkY2x1bXAKanlfYWxsJGNsdW1wX3Bsb3Rbd2hpY2goanlfYWxsJGNsdW1wX3R5cGUgIT0gIlRydWUgQ2x1bXAiKV0gPSAiTmFOIgpgYGAKCmBgYHtyfQpEb3RQbG90KGp5X2FsbCwgZmVhdHVyZXMgPSBjKCdDWENSNCcsICdMUlA4JywgJ0NYQ1I3JywgJ1ZMRExSJywgJ0VHRlInLCAnQ1hDTDEyJywgJ0NYQ0wxNCcsICdSRUxOJyksIGdyb3VwLmJ5ID0gImF2cCIpICsgUm90YXRlZEF4aXMoCikKYGBgCiMjIERlZmluZSB0aGUgc3BlY2lmaWMgY2x1bXAgdHlwZXMKYGBge3J9CgpsaW5lYXIgPSBjKCIxNjRfVENfMTBfMTIiLAoiMTY0X1RDXzEwXzUiLAoiMTY0X1RDXzFfMSIsCiIxNjRfVENfNV80IiwKIjE2NF9UQ183XzUiLAoiMTY0X1RDXzhfNyIsCiIxNjRfVENfOV8xIiwKIjE2NF9UQ185XzIiLAoiNDA4X0NDXzEyXzUiLAoiNDA4X1RDXzEwXzUiLAoiNDA4X1RDXzlfNSIpCgpwc3VlZG9saW5lYXIgPSBjKCI0MDhfQ29ydGljYWxfMV8wIiwKIjQwOF9UQ18xMF8zIiwKIjE2NF9UQ18xXzMiLAoiMTY0X1RDX0NvcnRpY2FsMV8xMSIsCiIxNjRfQ0NfOF8yIiwKIjE2NF9DQ185XzMiLAoiMTY0X0NDX0wyLTFfNSIsCiIxNjRfQ0NfTDItMl80IiwKIjE2NF9UQ184XzIiLAoiMTY0X1RDXzhfMyIsCiIxNjRfVENfMl8xIiwKIjE2NF9UQ180XzkiLAoiMTY0X1RDXzVfMiIsCiIxNjRfVENfNV85IiwKIjE2NF9UQ182XzAiLAoiMTY0X1RDXzdfNSIsCiIxNjRfVENfN18xIiwKIjE2NF9UQ183XzQiLAoiMTY0X1RDXzhfNCIsCiI0MDhfVENfMTRfNCIsCiI0MDhfVENfMl8wIiwKIjQwOF9UQ18yXzEyIiwKIjQwOF9UQ18zXzMiLAoiNDA4X1RDXzRfMTEiLAoiNDA4X1RDXzRfMTYiLAoiNDA4X3RjXzRfMSIsCiI0MDhfVENfNl81IiwKIjE2NF9UQ19Db3J0aWNhbF8yXzEiKQoKYmlnX2NsdW1wID0gYygiNDA4X1RDXzdfNCIsCiIxNjRfVENfMV8wIiwKIjE2NF9UQ18zXzEiLAoiNDA4X1RDXzExXzEiLAoiNDA4X1RDXzEyXzAiLAoiNDA4X1RDXzEzXzAiLAoiNDA4X1RDXzE0XzAiLAoiNDA4X1RDXzVfMyIsCiI0MDhfVENfMTVfMiIsCiI0MDhfVENfM18xIiwKIjQwOF9UQ18zXzAiLAoiMTY0X1RDXzZfNiIsCiI0MDhfVENfMTFfMSIsCiIxNjRfVENfQ29ydGljYWwxXzUiKQoKYGBgCgpgYGB7cn0KanlfYWxsJGNsdW1wX3R5cGUgPSAiTmFOIgpqeV9hbGwkY2x1bXBfdHlwZVt3aGljaChqeV9hbGwkY2x1bXAgIT0gIk5hTiIpXSA9ICJTbWFsbCBDbHVtcCIKanlfYWxsJGNsdW1wX3R5cGVbd2hpY2goanlfYWxsJGNsdW1wICVpbiUgbGluZWFyKV0gPSAiTGluZWFyIgpqeV9hbGwkY2x1bXBfdHlwZVt3aGljaChqeV9hbGwkY2x1bXAgJWluJSBwc3VlZG9saW5lYXIpXSA9ICJQc3VlZG8tTGluZWFyIgpqeV9hbGwkY2x1bXBfdHlwZVt3aGljaChqeV9hbGwkY2x1bXAgJWluJSBiaWdfY2x1bXApXSA9ICJUcnVlIENsdW1wIgpgYGAKYGBge3J9CkRvdFBsb3QoanlfYWxsLCBmZWF0dXJlcyA9IGMoJ0NYQ1I0JywgJ0xSUDgnLCAnQ1hDUjcnLCAnVkxETFInLCAnRUdGUicsICdDWENMMTInLCAnQ1hDTDE0JywgJ1JFTE4nKSwgZ3JvdXAuYnkgPSAiY2x1bXBfdHlwZSIpICsgUm90YXRlZEF4aXMoCikKYGBgCmBgYHtyfQpqeV9hbGwkY2x1bXBfcmVkdWNlZCA9ICdlcnJvcicKanlfYWxsJGNsdW1wX3JlZHVjZWRbd2hpY2goanlfYWxsJGNsdW1wX3R5cGUgJWluJSBjKCdUcnVlIENsdW1wJykpXSA9ICdDbHVtcCcKanlfYWxsJGNsdW1wX3JlZHVjZWRbd2hpY2goanlfYWxsJGNsdW1wX3R5cGUgJWluJSBjKCdOYU4nLCAnU21hbGwgQ2x1bXAnLCAnTGluZWFyJywgJ1BzdWVkby1MaW5lYXInKSldID0gJ0luZGl2aWR1YWwnCmBgYAoKYGBge3J9CmRwIDwtIERvdFBsb3QoanlfYWxsLCBmZWF0dXJlcyA9IGMoJ0NYQ1I0JywgJ0NYQ1I3JywgJ0VHRlInLCAnTFJQOCcsICdWTERMUicpLCBncm91cC5ieSA9ICJjbHVtcF9yZWR1Y2VkIikgKyBSb3RhdGVkQXhpcygpICsgeWxhYigiIikKCmdnc2F2ZShwbG90ID0gZHAsIGZpbGVuYW1lID0gJ2NsdW1wX2RvdF9wbG90LnBkZicsIHBhdGggPSBmaWxlLnBhdGgob3V0cHV0X2Rpcl9wbG90LCAnMjAyMjEyMTJfMScpLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA1LCB1bml0cyA9ICdjbScsIGRwaSA9IDMwMCkKYGBgCmBgYHtyfQpqeV9hbGwkY2x1bXBfc3RyZWFtIDwtIHBhc3RlKGp5X2FsbCRhcmVhLCBqeV9hbGwkY2x1bXBfdHlwZSkKYGBgCgpgYGB7cn0KanlfYWxsJGNsdW1wX2N0eXBlIDwtIHBhc3RlKElkZW50cyhqeV9hbGwpLCBqeV9hbGwkY2x1bXBfcmVkdWNlZCkKYGBgCgoKYGBge3J9CiNEb3RQbG90KGp5X2FsbFssIHdoaWNoKGdyZXBsKCJUQyIsIGp5X2FsbCRhcmVhKSAmIGdyZXBsKCIxNjQiLCBqeV9hbGwkYXJlYSkpXSwgZmVhdHVyZXMgPSBjKCdDWENSNCcsICdMUlA4JywgJ0NYQ1I3JywgJ1ZMRExSJywgJ0VHRlInLCAnQ1hDTDEyJywgJ0NYQ0wxNCcsICdSRUxOJyksIGdyb3VwLmJ5ID0gImNsdW1wX2N0eXBlIikgKyBSb3RhdGVkQXhpcygpICsgY29vcmRfZmxpcCgpCkRvdFBsb3QoanlfYWxsLCBmZWF0dXJlcyA9IGMoJ0NYQ1I0JywgJ0xSUDgnLCAnQ1hDUjcnLCAnVkxETFInLCAnRUdGUicsICdDWENMMTInLCAnQ1hDTDE0JywgJ1JFTE4nKSwgZ3JvdXAuYnkgPSAiY2x1bXBfY3R5cGUiKSArIFJvdGF0ZWRBeGlzKCkgKyBjb29yZF9mbGlwKCkKYGBgCmBgYHtyfQpkcCA8LSBEb3RQbG90KGp5X2FsbFssIHdoaWNoKGdyZXBsKCdNUycsIGp5X2FsbCRhcmVhKSldLCBmZWF0dXJlcyA9IGMoJ0NYQ1I0JywgJ0xSUDgnLCAnQ1hDUjcnLCAnVkxETFInLCAnRUdGUicpLCBncm91cC5ieSA9ICJjbHVtcF9zdHJlYW0iKSArIFJvdGF0ZWRBeGlzKCkgKyB5bGFiKCIiKQpnZ3NhdmUocGxvdCA9IGRwLCBmaWxlbmFtZSA9ICdjbHVtcF9kb3RfcGxvdF90eXBlc3RyZWFtLnBkZicsIHBhdGggPSBmaWxlLnBhdGgob3V0cHV0X2Rpcl9wbG90LCAnMjAyMjEyMTJfMScpLCB3aWR0aCA9IDE1LCBoZWlnaHQgPSAxNSwgdW5pdHMgPSAnY20nLCBkcGkgPSAzMDApCmBgYAoKIyMgRG8gYSBjaG9yZCBkaWFncmFtIHRoYXQgeW91IGxpa2UKCiMjICAgZnJvbSB0byB2YWx1ZQojIyAxICAgIGEgIEEgICAgIDEKIyMgMiAgICBiICBCICAgICAyCiMjIDMgICAgYyAgQyAgICAgMwoKU28gZm9yIG1lLCBJIG5lZWQgZnJvbSBjZWxsIHR5cGUgMSB0byBjZWxsIHR5cGUgMgoKQmFzaWNhbGx5Cgpmcm9tICAgdG8uICB2YWx1ZQpDVDEuICAgQ1QxCkNUMSBDVDIKQ1QxIENUMwoKYGBge3J9CmRwIDwtIERvdFBsb3QoanlfYWxsWywgd2hpY2goZ3JlcGwoJ01TJywganlfYWxsJGFyZWEpKV0sIGZlYXR1cmVzID0gYygnQ1hDUjQnLCAnTFJQOCcsICdDWENSNycsICdWTERMUicsICdFR0ZSJyksIGdyb3VwLmJ5ID0gImNsdW1wX3R5cGUiKSArIFJvdGF0ZWRBeGlzKCkgKyB5bGFiKCIiKQpnZ3NhdmUocGxvdCA9IGRwLCBmaWxlbmFtZSA9ICdjbHVtcF9kb3RfcGxvdF90eXBlLnBkZicsIHBhdGggPSBmaWxlLnBhdGgob3V0cHV0X2Rpcl9wbG90LCAnMjAyMjEyMTJfMScpLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMCwgdW5pdHMgPSAnY20nLCBkcGkgPSAzMDApCmBgYAoKCmBgYHtyfQpjbGFzc2VzIDwtIGxldmVscyhJZGVudHMoanlfYWxsKSkKbiA9IGxlbmd0aChjbGFzc2VzKQpjMSA9IHJlcChjbGFzc2VzLCBuKQpjMiA9IHJlcChjbGFzc2VzLCBlYWNoPW4pCmNfZGYgPC0gYXMuZGF0YS5mcmFtZShjYmluZChjMSwgYzIpKQpgYGAKCmBgYHtyfQpYWV8xNjQgPSBFbWJlZGRpbmdzKGp5XzE2NCwgJ0gnKQpuZWlnaGJvcnMgPSA1Cgpubl9ncmFwaCA9IG5uZyhYWV8xNjQsIGsgPSBuZWlnaGJvcnMpCgpYWV80MDggPSBFbWJlZGRpbmdzKGp5XzQwOCwgJ0gnKQpubl9ncmFwaDIgPSBubmcoWFlfNDA4LCBrID0gbmVpZ2hib3JzKQoKY2x1c3RlcnNfMTY0ID0gSWRlbnRzKGp5X2FsbClbMTpuY29sKGp5XzE2NCldCklkZW50cyhqeV8xNjQpID0gY2x1c3RlcnNfMTY0CmNsdXN0ZXJzXzQwOCA9IElkZW50cyhqeV9hbGwpWyhuY29sKGp5XzE2NCkrMSk6bmNvbChqeV9hbGwpXQpJZGVudHMoanlfNDA4KSA9IGNsdXN0ZXJzXzQwOApgYGAKCgpgYGB7cn0KCmZpbGxfYWRqX21hdHJpeCA8LSBmdW5jdGlvbihubl9ncmFwaCwganlfb2JqLCBhZGpfbWF0LCB1bmRpcmVjdGVkID0gVFJVRSl7CiAgYWRqX21hdCR2YWwgPSAwCiAgYWRkZWQgPSBhcy5kYXRhLmZyYW1lKG1hdHJpeCgtMSwgMSwgMikpCiAgY29sbmFtZXMoYWRkZWQpID0gYygnYzEnLCAnYzInKQogIGZvcihpIGluIDE6bmNvbChqeV9vYmopKXsKICAgIGNsYXNzX2NlbGwgPSBJZGVudHMoanlfb2JqKVtpXQogICAgIyMgR2V0IHRoZSBjZWxscyB0aGF0IGFyZSB0aGUgc2FtZQogICAgbmVhcmVzdF9uZWlnaGJvcnMgPSBhcy5tYXRyaXgobm5fZ3JhcGhbW2ldXSkKICAgIG5laWdoYm9yX2lkZW50cyA9IElkZW50cyhqeV9vYmopW25lYXJlc3RfbmVpZ2hib3JzW1sxXV1dCiAgICBmb3IobiBpbiAxOmxlbmd0aChuZWlnaGJvcl9pZGVudHMpKXsKICAgICAgI3ByaW50KGMoaSwgbikpCiAgICAgIG5faWQgPSBhcy5udW1lcmljKG5lYXJlc3RfbmVpZ2hib3JzW1sxXV1bbl0pCiAgICAgIG5laWdoYm9yID0gbmVpZ2hib3JfaWRlbnRzW25dCiAgICAgIGlmKGxlbmd0aChhZGRlZCRjMlt3aGljaChhZGRlZCRjMSA9PSBpKV0gPT0gbl9pZCkgPiAwICYmCiAgICAgICAgIGFkZGVkJGMyW3doaWNoKGFkZGVkJGMxID09IGkpXSA9PSBuX2lkICYmIHVuZGlyZWN0ZWQKICAgICAgICAgKXsKICAgICAgICAgICNwcmludCgnZHVwJykKICAgICAgIH0gZWxzZXsKICAgICAgICAgI3ByaW50KCd5dXAnKQogICAgICAgIHZhbHVlID0gYWRqX21hdCR2YWxbd2hpY2goYWRqX21hdCRjMSA9PSBjbGFzc19jZWxsICYgYWRqX21hdCRjMiA9PSBuZWlnaGJvcildCiAgICAgICAgYWRqX21hdCR2YWxbd2hpY2goYWRqX21hdCRjMSA9PSBjbGFzc19jZWxsICYgYWRqX21hdCRjMiA9PSBuZWlnaGJvcildID0gdmFsdWUgKyAxCiAgICAgICAgYWRkZWQgPC0gYWRkZWQgJT4lIGFkZF9yb3coYzEgPSBuX2lkLCBjMiA9IGkpCiAgICAgIH0KICAgIH0KICB9CiAgcmV0dXJuKGFkal9tYXQpCn0KCm5vcm1fYWRqX21hdCA8LSBmdW5jdGlvbihqeV9vYmosIGFkal9tYXQpewogIGlkZW50cyA8LSBsZXZlbHMoSWRlbnRzKGp5X29iaikpCiAgZm9yKGlkZW50IGluIGlkZW50cyl7CiAgICAgaWRfY3QgPC0gc3VtKElkZW50cyhqeV9vYmopID09IGlkZW50KSAqIG5laWdoYm9ycwogICAgIGFkal9tYXRbd2hpY2goYWRqX21hdCRjMSA9PSBpZGVudCksIDNdID0gYXMubnVtZXJpYyhhZGpfbWF0W3doaWNoKGFkal9tYXQkYzEgPT0gaWRlbnQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDNdKSAvIGlkX2N0CiAgfQogIHJldHVybihhZGpfbWF0KQp9CgoKYWRqX21hdDEgPC0gZmlsbF9hZGpfbWF0cml4KG5uX2dyYXBoLCBqeV8xNjQsIGNfZGYpCmFkal9tYXQyIDwtIGZpbGxfYWRqX21hdHJpeChubl9ncmFwaDIsIGp5XzQwOCwgY19kZikKCiNhZGpfbWF0MXAgPC1ub3JtX2Fkal9tYXQoanlfMTY0LCBhZGpfbWF0MSkKI2Fkal9tYXQycCA8LW5vcm1fYWRqX21hdChqeV80MDgsIGFkal9tYXQyKQpgYGAKCmBgYHtyfQphZGpfbWF0X2ZpbmFsIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoYzEsIGMyLCBhZGpfbWF0MSR2YWwgKyBhZGpfbWF0MiR2YWwpKQojYWRqX21hdF9maW5hbCA8LSBub3JtX2Fkal9tYXQoanlfYWxsLCBhZGpfbWF0X2ZpbmFsKQpjb2xuYW1lcyhhZGpfbWF0X2ZpbmFsKSA8LSBjKCdmcm9tJywgJ3RvJywgJ3ZhbHVlJykKYWRqX21hdF9maW5hbCR2YWx1ZSA8LSBhcy5udW1lcmljKGFkal9tYXRfZmluYWwkdmFsdWUpCmBgYAoKYGBge3J9CmNpcmNvcy5jbGVhcigpCmNob3JkRGlhZ3JhbShhZGpfbWF0X2ZpbmFsLCBzZWxmLmxpbmsgPSAyKQpgYGAKYGBge3J9CmRmMiA9IGRhdGEuZnJhbWUoc3RhcnQgPSBjKCJhIiwgImIiLCAiYyIsICJhIiksIGVuZCA9IGMoImEiLCAiYSIsICJiIiwgImMiKSkKY2hvcmREaWFncmFtKGRmMiwgZ3JpZC5jb2wgPSAxOjMsIHNlbGYubGluayA9IDEpCnRpdGxlKCJzZWxmLmxpbmsgPSAxIikKY2hvcmREaWFncmFtKGRmMiwgZ3JpZC5jb2wgPSAxOjMsIHNlbGYubGluayA9IDIpCnRpdGxlKCJzZWxmLmxpbmsgPSAyIikKCmBgYApgYGB7cn0KbWF0IDwtIGRjYXN0KGFkal9tYXRfZmluYWwsIGZyb20gfiB0bywgdmFsdWUudmFyID0gInZhbHVlIikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJmcm9tIikgJT4lCiAgI211dGF0ZV9hbGwoLmZ1bnMgPSByZWFkcjo6cGFyc2VfbnVtYmVyKSAlPiUKICBhcy5tYXRyaXgoKSAKYGBgCmBgYHtyfQpjaXJjb3MuY2xlYXIoKQpjaG9yZERpYWdyYW0obWF0LCBzZWxmLmxpbmsgPSAxLCBncmlkLmNvbCA9IGdldF9jbHVzdGVyX2NvbG9ycyhyb3duYW1lcyhtYXQpKSwgY29sID0gY29sX21hdCkKYGBgCmBgYHtyfQpjb2xfbWF0ID0gbWF0cml4KCIjZTVlNWU1IiwgbnJvdyA9IG5yb3cobWF0KSwgbmNvbCA9IG5jb2wobWF0KSkKZGlhZyhjb2xfbWF0KSA8LSBnZXRfY2x1c3Rlcl9jb2xvcnMocm93bmFtZXMobWF0KSkKI2RpbShjb2xfbWF0KSA9IGRpbShtYXQpICAjIHRvIG1ha2Ugc3VyZSBpdCBpcyBhIG1hdHJpeApgYGAKCmBgYHtyfQpYWWRfMTY0ID0gRW1iZWRkaW5ncyhqeV8xNjRbLCB3aGljaChncmVwbCgnQ0MnLCBqeV8xNjQkYXJlYSkgJiBncmVwbCgnTVMnLCBqeV8xNjQkYXJlYSkpXSwgJ0gnKQpYWXZfMTY0ID0gRW1iZWRkaW5ncyhqeV8xNjRbLCB3aGljaChncmVwbCgnVEMnLCBqeV8xNjQkYXJlYSkgJiBncmVwbCgnTVMnLCBqeV8xNjQkYXJlYSkpXSwgJ0gnKQoKbm5kX2dyYXBoID0gbm5nKFhZZF8xNjQsIGsgPSBuZWlnaGJvcnMpCm5udl9ncmFwaCA9IG5uZyhYWXZfMTY0LCBrID0gbmVpZ2hib3JzKQoKWFlkXzQwOCA9IEVtYmVkZGluZ3MoanlfNDA4Wywgd2hpY2goZ3JlcGwoJ0NDJywganlfNDA4JGFyZWEpICYgZ3JlcGwoJ01TJywganlfNDA4JGFyZWEpKV0sICdIJykKWFl2XzQwOCA9IEVtYmVkZGluZ3MoanlfNDA4Wywgd2hpY2goZ3JlcGwoJ1RDJywganlfNDA4JGFyZWEpICYgZ3JlcGwoJ01TJywganlfNDA4JGFyZWEpKV0sICdIJykKCm5uZF9ncmFwaDIgPSBubmcoWFlkXzQwOCwgayA9IG5laWdoYm9ycykKbm52X2dyYXBoMiA9IG5uZyhYWXZfNDA4LCBrID0gbmVpZ2hib3JzKQpgYGAKCmBgYHtyfQpzdHJlYW0gPSAnQ0MnCgphZGpfbWF0MSA8LSBmaWxsX2Fkal9tYXRyaXgobm5fZ3JhcGgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAganlfMTY0Wywgd2hpY2goZ3JlcGwoc3RyZWFtLCBqeV8xNjQkYXJlYSkgJiBncmVwbCgnTVMnLCBqeV8xNjQkYXJlYSkpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjX2RmLCB1bmRpcmVjdGVkID0gRkFMU0UpCmFkal9tYXQyIDwtIGZpbGxfYWRqX21hdHJpeChubl9ncmFwaDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAganlfNDA4Wywgd2hpY2goZ3JlcGwoc3RyZWFtLCBqeV80MDgkYXJlYSkgJiBncmVwbCgnTVMnLCBqeV80MDgkYXJlYSkpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjX2RmLCB1bmRpcmVjdGVkID0gRkFMU0UpCmBgYApgYGB7cn0KYWRqX21hdF9maW5hbCA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKGMxLCBjMiwgYWRqX21hdDEkdmFsICsgYWRqX21hdDIkdmFsKSkKI2Fkal9tYXRfZmluYWwgPC0gbm9ybV9hZGpfbWF0KGp5X2FsbFssIHdoaWNoKGdyZXBsKHN0cmVhbSwganlfYWxsJGFyZWEpICYgZ3JlcGwoJ01TJywganlfYWxsJGFyZWEpKV0sIGFkal9tYXRfZmluYWwpCmNvbG5hbWVzKGFkal9tYXRfZmluYWwpIDwtIGMoJ2Zyb20nLCAndG8nLCAndmFsdWUnKQphZGpfbWF0X2ZpbmFsJHZhbHVlID0gYXMubnVtZXJpYyhhZGpfbWF0X2ZpbmFsJHZhbHVlKQpgYGAKCmBgYHtyfQpjaGVja19hZGpfcHJvYiA8LSBmdW5jdGlvbihhZGpfbWF0KXsKICB0eXBlcyA9IHVuaXF1ZShhZGpfbWF0JGZyb20pCiAgZm9yKGN0IGluIHR5cGVzKXsKICAgIHNtIDwtIHN1bShhZGpfbWF0W3doaWNoKGFkal9tYXQkZnJvbSA9PSBjdCksIDNdKQogICAgcHJpbnQoc20pCiAgfQp9CmNoZWNrX2Fkal9wcm9iKGFkal9tYXRfZmluYWwpCmBgYAoKCmBgYHtyfQptYXQgPC0gZGNhc3QoYWRqX21hdF9maW5hbCwgZnJvbSB+IHRvLCB2YWx1ZS52YXIgPSAidmFsdWUiKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImZyb20iKSAlPiUKICAjbXV0YXRlX2FsbCguZnVucyA9IHJlYWRyOjpwYXJzZV9udW1iZXIpICU+JQogIGFzLm1hdHJpeCgpIAoKY2lyY29zLmNsZWFyKCkKY2hvcmREaWFncmFtKG1hdCwgc2VsZi5saW5rID0gMSwgZ3JpZC5jb2wgPSBnZXRfY2x1c3Rlcl9jb2xvcnMocm93bmFtZXMobWF0KSksIGNvbCA9IGNvbF9tYXQpCmBgYApPa2F5LCBzbyBJIG5lZWQgYSBub2RlSUQgZm9yIGVhY2ggInVuaXF1ZSB0eXBlIiwgc28ganVzdCB0aGUgY2VsbCB0eXBlcwoKYGBge3J9CiMjIyBOZWVkIHRvIHNldCB1cCB0aGUgZDogZWRnZXMKIyMjIGFuZCBzZXQgdGhlIHZlcnRpY2VzOiB3aGljaCBhcmUgdGhlIG5vZGUgSURzCiNhZGpfbWF0X2ZpbmFsCm5ldCA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgobWF0LCBtb2RlID0gImRpcmVjdGVkIiwgd2VpZ2h0ZWQgPSBUUlVFKQpgYGAKYGBge3J9Cm5ldCA8LSBzaW1wbGlmeShuZXQsIHJlbW92ZS5tdWx0aXBsZSA9IEYsIHJlbW92ZS5sb29wcyA9IFQpCmBgYAoKYGBge3J9CnBsb3QobmV0LCBlZGdlLmFycm93LnNpemU9LjQsIGVkZ2UuY3VydmVkPS41KQpgYGAKYGBge3J9ClYobmV0KSRjb2xvciA8LSBnZXRfY2x1c3Rlcl9jb2xvcnMoVihuZXQpJG5hbWUpCgpWKG5ldCkkc2l6ZSA8LSBsb2cocm91bmQocm93U3VtcyhtYXQpIC8gbmVpZ2hib3JzKSkgKiA3CgojIFRoZSBsYWJlbHMgYXJlIGN1cnJlbnRseSBub2RlIElEcy4KIyBTZXR0aW5nIHRoZW0gdG8gTkEgd2lsbCByZW5kZXIgbm8gbGFiZWxzOgpWKG5ldCkkbGFiZWwgPC0gTkEKCiMgU2V0IGVkZ2Ugd2lkdGggYmFzZWQgb24gd2VpZ2h0OgpFKG5ldCkkd2lkdGggPC0gbG9nKEUobmV0KSR3ZWlnaHQpCgojY2hhbmdlIGFycm93IHNpemUgYW5kIGVkZ2UgY29sb3I6CkUobmV0KSRhcnJvdy5zaXplIDwtIC4yCkUobmV0KSRlZGdlLmNvbG9yIDwtICJncmF5ODAiCgojIFdlIGNhbiBldmVuIHNldCB0aGUgbmV0d29yayBsYXlvdXQ6CmdyYXBoX2F0dHIobmV0LCAibGF5b3V0IikgPC0gbGF5b3V0X3dpdGhfZnIobmV0KSNuZXQsIGNlbnRlciA9ICdDR0UvTEdFJykKcGxvdChuZXQsIGVkZ2UuYXJyb3cuc2l6ZT0uNCwgZWRnZS5jdXJ2ZWQ9LjUpCmBgYAojIyMgRmlyc3QgbWFlayBhIGhlYXRtYXAgZnJvbSBpbWFnZXMgYW5kIGNlbGwgdHlwZXMKYGBge3J9Cmp5X2FsbCRzbGljZSA8LSBOQQpqeV9hbGwkc2xpY2VbMTpuY29sKGp5XzE2NCldIDwtICcxNjQnCmp5X2FsbCRzbGljZVsobmNvbChqeV8xNjQpKzEpOm5jb2woanlfYWxsKV0gPC0gJzQwOCcKanlfYWxsJGltZ3NsaWNlIDwtIHBhc3RlMChqeV9hbGwkc2xpY2UsICJfIiwganlfYWxsJGltYWdlKQoKaWRzIDwtIGxldmVscyhJZGVudHMoanlfYWxsKSkKaW1ncyA8LSB1bmlxdWUoanlfYWxsJGltZ3NsaWNlKQpqeV9tYXRyaXggPC0gbWF0cml4KC0xLCBsZW5ndGgoaW1ncyksIGxlbmd0aChpZHMpKQpmb3IoaSBpbiAxOmxlbmd0aChpbWdzKSl7CiAgaW1nIDwtIGltZ3NbaV0KICBvYmogPC0ganlfYWxsWywgd2hpY2goanlfYWxsJGltZ3NsaWNlID09IGltZyldCiAgdGIgPC0gdGFibGUoZmFjdG9yKElkZW50cyhvYmopLCBsZXZlbHMgPSBpZHMpKQogIGp5X21hdHJpeFtpLCBdIDwtIHRiIC8gc3VtKHRiKQp9CmBgYApgYGB7ciBmaWcuaGVpZ2h0ID0gMiwgZmlnLndpZHRoID0gNn0Kcm93bmFtZXMoanlfbWF0cml4KSA8LSBpbWdzCmNvbG5hbWVzKGp5X21hdHJpeCkgPC0gaWRzCnBoZWF0bWFwKHQoanlfbWF0cml4KSwgY2VsbHdpZHRoID0gMTAsIGNlbGxoZWlnaHQgPSAxMCwgZm9udHNpemUgPSAxMCwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIGNsdXN0ZXJfcm93cyA9IEZBTFNFKQpgYGAKYGBge3J9CmJhcnBsb3QodGFibGUoSWRlbnRzKGp5X2FsbCkpLCBjb2wgPSBnZXRfY2x1c3Rlcl9jb2xvcnMobGV2ZWxzKElkZW50cyhqeV9hbGwpKSksIGNleC5uYW1lcz0wLjcpCmBgYAojIyMgQmVmb3JlIHBlcm11dGF0aW9uLCBsZXRzIGp1c3QgY29uZmlybSB0aGF0IGFsbCBuZWlnaGJvcnMgYXJlIGluIHNhbWUgaW1hZ2UKV2UgaGF2ZSBmb3VuZCB0aGF0IDY5IGNlbGxzIGhhdmUgbmVpZ2hib3JzIGluIG90aGVyIGltYWdlcy4gVGhpcyBzaG91bGQgbm90IGJlCnRoZSBjYXNlIGFuZCBzaG91bGQgYmUgZml4ZWQgaW4gZm9sbG93aW5nIGFuYWx5c2VzLgpgYGB7cn0KCnNhbWVfaW1nX2NoZWNrIDwtIGZ1bmN0aW9uKG5uX2dyYXBoLCBqeV9vYmopewogIGNvdW50ZXIgPSAwCiAgZm9yKGkgaW4gMTpuY29sKGp5X29iaikpewogICAgY2VsbF9pbWcgPSBqeV9vYmokaW1hZ2VbaV0KICAgICMjIEdldCB0aGUgY2VsbHMgdGhhdCBhcmUgdGhlIHNhbWUKICAgIG5lYXJlc3RfbmVpZ2hib3JzID0gYXMubWF0cml4KG5uX2dyYXBoW1tpXV0pCiAgICBuZWlnaGJvcl9pbWdzID0ganlfb2JqJGltYWdlW25lYXJlc3RfbmVpZ2hib3JzW1sxXV1dCiAgICBpZihhbnkobmVpZ2hib3JfaW1ncyAhPSBjZWxsX2ltZykpewogICAgICBjb3VudGVyID0gY291bnRlciArIDEKICAgIH0KICB9CiAgcmV0dXJuKGNvdW50ZXIpCn0KcHJpbnQoc2FtZV9pbWdfY2hlY2sobm5fZ3JhcGgsIGp5XzE2NCkpCmBgYAoKCiMjIyBOZWVkIHRvIHJ1biBhIHBlcm11dGF0aW9uIHRlc3QKYGBge3J9CgpnZXRfaW50ZXJhY3Rpb25zIDwtIGZ1bmN0aW9uKGtubl9ncmFwaCwgb2JqLCBjbGFzc2VzLCBjX2RmKXsKICAgIGNfZGYkdmFsID0gMAogICAgZm9yKGkgaW4gMTpuY29sKG9iaikpewogICAgICBjbGFzc19jZWxsID0gSWRlbnRzKG9iailbaV0KICAgICAgIyMgR2V0IHRoZSBjZWxscyB0aGF0IGFyZSB0aGUgc2FtZQogICAgICBuZWFyZXN0X25laWdoYm9ycyA9IGFzLm1hdHJpeChubl9ncmFwaFtbaV1dKQogICAgICBuZWlnaGJvcl9pZGVudHMgPSBJZGVudHMob2JqKVtuZWFyZXN0X25laWdoYm9yc1tbMV1dXQogICAgICBmb3IobmVpZ2hib3IgaW4gbmVpZ2hib3JfaWRlbnRzKXsKICAgICAgICB2YWx1ZSA9IGNfZGYkdmFsW3doaWNoKGNfZGYkYzEgPT0gY2xhc3NfY2VsbCAmIGNfZGYkYzIgPT0gbmVpZ2hib3IpXQogICAgICAgIGNfZGYkdmFsW3doaWNoKGNfZGYkYzEgPT0gY2xhc3NfY2VsbCAmIGNfZGYkYzIgPT0gbmVpZ2hib3IpXSA9IHZhbHVlICsgMQogICAgICB9CiAgICB9CiAgICByZXR1cm4oY19kZikKfQojIyMgSXRlcmF0ZSB0aHJvdWdoIGFsbCBpbWFnZXMKIyMjIHJ1biAxMDAgcGVybXV0YXRpb25zLCBhbmQgZ2V0IHRoZSBwIHZhbHVlIGZyb20gdGhlIHJlYWwgZGF0YQpwZXJtdXRhdGlvbnMgPSA5OQpuZWlnaGJvcnMgPSA1CgojIyMgTmVlZCB0byBzZXQgdXAgdGhlIGRhdGFmcmFtZQpjbGFzc2VzIDwtIGxldmVscyhJZGVudHMoanlfYWxsKSkKbiA9IGxlbmd0aChjbGFzc2VzKQpjMSA9IHJlcChjbGFzc2VzLCBuKQpjMiA9IHJlcChjbGFzc2VzLCBlYWNoPW4pCmNfZGYgPC0gYXMuZGF0YS5mcmFtZShjYmluZChjMSwgYzIpKQoKIyMgd2lsbCBzYXZlIGFsbCBpbnRvIDIgZGlmZmVyZW50IG1hdHJpY2VzCmludGVyYWN0aW9uX21hdCA8LSBtYXRyaXgoLTEsIG5yb3coY19kZiksIGxlbmd0aChpbWdzKSkgCmF2b2lkYW5jZV9tYXQgPC0gbWF0cml4KC0xLCBucm93KGNfZGYpLCBsZW5ndGgoaW1ncykpIAoKIyMgTmVlZCB0byBjcmVhdGUgYSBqeV9hbGwgZW1iZWRkaW5nCmVtYmVkXzE2NCA8LSBFbWJlZGRpbmdzKGp5XzE2NCwgIkgiKQplbWJlZF80MDggPC0gRW1iZWRkaW5ncyhqeV80MDgsICJIIikKanlfYWxsW1siSCJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gcmJpbmQoZW1iZWRfMTY0LCBlbWJlZF80MDgpLCBrZXkgPSAicGl4ZWxfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkoanlfYWxsKSkKCmZvcihpIGluIDE6bGVuZ3RoKGltZ3MpKXsKICBwcmludChwYXN0ZSgiUGVybXV0aW5nIGltYWdlIiwgaSwgIm9mIiwgbGVuZ3RoKGltZ3MpKSkKICBpbWcgPC0gaW1nc1tpXQogIG9iaiA8LSBqeV9hbGxbLCB3aGljaChqeV9hbGwkaW1nc2xpY2UgPT0gaW1nKV0KICAjIyMgQ3JlYXRlIG5uIGdyYXBoIGZvciByZWFsCiAgWFlfb2JqID0gRW1iZWRkaW5ncyhvYmosICdIJykKICBubl9ncmFwaCA9IG5uZyhYWV9vYmosIGsgPSBuZWlnaGJvcnMpCiAgIyMjIEl0ZXJhdGUgdGhyb3VnaCBlYWNoIGNlbGwgdHlwZQogICMjIyBDb21wdXRlIHRoZSBtZWFuIGZvciB0aGlzIG9uZQogIGRhdGFfY3RzIDwtIGdldF9pbnRlcmFjdGlvbnMobm5fZ3JhcGgsIG9iaiwgY2xhc3NlcywgY19kZikKICBvYmpfaWRlbnRzIDwtIElkZW50cyhvYmopCiAgcGVybV9tYXRyaXggPSBtYXRyaXgoTkEsIG5yb3coY19kZiksIHBlcm11dGF0aW9ucykKICBmb3IocGVybXV0YXRpb24gaW4gMTpwZXJtdXRhdGlvbnMpewogICAgSWRlbnRzKG9iaikgPC0gc2FtcGxlKElkZW50cyhvYmopKQogICAgcGVybV9jdHMgPC0gZ2V0X2ludGVyYWN0aW9ucyhubl9ncmFwaCwgb2JqLCBjbGFzc2VzLCBjX2RmKQogICAgcGVybV9tYXRyaXhbLCBwZXJtdXRhdGlvbl0gPC0gcGVybV9jdHMkdmFsCiAgfQogIGludGVyYWN0aW9uX21hdFssIGldIDwtIHJvd1N1bXMocGVybV9tYXRyaXggPD0gZGF0YV9jdHMkdmFsKSAvIChwZXJtdXRhdGlvbnMgKyAxKQogIGF2b2lkYW5jZV9tYXRbLCBpXSA8LSByb3dTdW1zKHBlcm1fbWF0cml4ID49IGRhdGFfY3RzJHZhbCkgLyAocGVybXV0YXRpb25zICsgMSkKICAjIyBjb3VudCB0aGUgaWRlbnRpdGllcwogIHRibCA8LSB0YWJsZShJZGVudHMob2JqKSkKICAjIyBJZiB0aGUgY2VsbCB0eXBlIGRvZXMgbm90IGV4aXN0IGluIHRoZSBpbWFnZSwgZG8gbm90IGNvdW50IHRoZSBQIHZhbHVlIGZvciB0aGUKICAjIyBpbnRlcmFjdGlvbi4uLiBvYnZpb3VzbHkKICBub25leGlzdF9peHMgPC0gd2hpY2goIShjX2RmJGMxICVpbiUgbmFtZXModGJsKSkgfCAhKGNfZGYkYzIgJWluJSBuYW1lcyh0YmwpKSkKICBpbnRlcmFjdGlvbl9tYXRbbm9uZXhpc3RfaXhzLCBpXSA8LSBOQQogIGF2b2lkYW5jZV9tYXRbbm9uZXhpc3RfaXhzLCBpXSA8LSBOQQp9CmBgYApgYGB7cn0KIyMgU2F2ZSB0aGlzIGRpZmZpY3VsdGx5IG1hZGUgZmlsZXMKc2F2ZVJEUyhpbnRlcmFjdGlvbl9tYXQsIGZpbGUucGF0aCgnL2hvbWUvYXVub3kvc3QvYXJjX3Byb2ZpbGluZy9zdF9hbmFseXNpcy9oYW5kX2Fubm90YXRlZF9kYXRhLycsICdpbnRlcmFjdGlvbl9tYXQucmRzJykpCnNhdmVSRFMoYXZvaWRhbmNlX21hdCwgZmlsZS5wYXRoKCcvaG9tZS9hdW5veS9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvJywgJ2F2b2lkYW5jZV9tYXQucmRzJykpCmBgYAoKYGBge3J9CnBsb3RfbWFwIDwtIGludGVyYWN0aW9uX21hdApwbG90X21hcFt3aGljaChwbG90X21hcCA+PSAwLjA1KV0gPSBOQQpwaGVhdG1hcChwbG90X21hcCwgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjZXggPSAwLjgpCmBgYAoKYGBge3J9CnN1bShpbnRlcmFjdGlvbl9tYXQgPCAwLjA1LCBuYS5ybSA9IFRSVUUpCmBgYAoKYGBge3J9Cm1pbihyb3dNZWFucyhpbnRlcmFjdGlvbl9tYXQsIG5hLnJtID0gVFJVRSkpCmBgYAoKYGBge3J9CnJvd25hbWVzKGF2b2lkYW5jZV9tYXQpIDwtIHBhc3RlMChjX2RmJGMxLCAiLT4iLCBjX2RmJGMyKQpjb2xuYW1lcyhhdm9pZGFuY2VfbWF0KSA8LSBpbWdzCnJvd25hbWVzKGludGVyYWN0aW9uX21hdCkgPC0gcGFzdGUwKGNfZGYkYzEsICItPiIsIGNfZGYkYzIpCmNvbG5hbWVzKGludGVyYWN0aW9uX21hdCkgPC0gaW1ncwpgYGAKCmBgYHtyfQp2bXNfbGFiZWxzIDwtIGMocGFzdGUwKCIxNjRfIiwgYW50cl9UQ19NUyksIHBhc3RlMCgiNDA4XyIsIHBvc3RfVENfTVMpKQpkbXNfbGFiZWxzIDwtIGMocGFzdGUwKCIxNjRfIiwgYW50cl9DQ19NUyksIHBhc3RlMCgiNDA4XyIsIHBvc3RfQ0NfTVMpKQoKc3ViX21hdCA8LSBpbnRlcmFjdGlvbl9tYXRbLCBkbXNfbGFiZWxzXQphdmdzIDwtIHJvd01lYW5zKHN1Yl9tYXQgPCAwLjA1LCBuYS5ybSA9IFRSVUUpCiNhdmdzCmBgYApgYGB7cn0KaW50ZXJhY3Rpb25fZGYgPC0gY19kZiAlPiUgYWRkX2NvbHVtbih2YWx1ZSA9IGF2Z3MpCmNvbG5hbWVzKGludGVyYWN0aW9uX2RmKSA8LSBjKCdmcm9tJywgJ3RvJywgJ3ZhbHVlJykKbWF0IDwtIGRjYXN0KGludGVyYWN0aW9uX2RmLCBmcm9tIH4gdG8sIHZhbHVlLnZhciA9ICJ2YWx1ZSIpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygiZnJvbSIpICU+JQogICNtdXRhdGVfYWxsKC5mdW5zID0gcmVhZHI6OnBhcnNlX251bWJlcikgJT4lCiAgYXMubWF0cml4KCkgCmBgYApgYGB7cn0KbmV0IDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChtYXQsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IFRSVUUpCmBgYAoKYGBge3J9CnBsb3QobmV0KQpgYGAKYGBge3J9ClYobmV0KSRjb2xvciA8LSBnZXRfY2x1c3Rlcl9jb2xvcnMoVihuZXQpJG5hbWUpCgpWKG5ldCkkc2l6ZSA8LSB0YWJsZShJZGVudHMoanlfYWxsWywgd2hpY2goanlfYWxsJGltZ3NsaWNlICVpbiUgZG1zX2xhYmVscyldKSlbVihuZXQpJG5hbWVdIC8gNQoKIyBUaGUgbGFiZWxzIGFyZSBjdXJyZW50bHkgbm9kZSBJRHMuCiMgU2V0dGluZyB0aGVtIHRvIE5BIHdpbGwgcmVuZGVyIG5vIGxhYmVsczoKVihuZXQpJGxhYmVsIDwtIE5BCgojIFNldCBlZGdlIHdpZHRoIGJhc2VkIG9uIHdlaWdodDoKRShuZXQpJHdpZHRoIDwtIEUobmV0KSR3ZWlnaHQgKiAxNQoKI2NoYW5nZSBhcnJvdyBzaXplIGFuZCBlZGdlIGNvbG9yOgpFKG5ldCkkYXJyb3cuc2l6ZSA8LSAuMgpFKG5ldCkkZWRnZS5jb2xvciA8LSAiYmx1ZTEiCgojIFdlIGNhbiBldmVuIHNldCB0aGUgbmV0d29yayBsYXlvdXQ6CmdyYXBoX2F0dHIobmV0LCAibGF5b3V0IikgPC0gbGF5b3V0X2FzX3N0YXIobmV0LCBjZW50ZXIgPSAnQ0dFL0xHRScpCnBsb3QobmV0LCBlZGdlLmN1cnZlZD0uMSkKYGBgCgpgYGB7cn0KcGhlYXRtYXAoYXZvaWRhbmNlX21hdCwgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjZXggPSAwLjgpCmBgYAoKYGBge3J9CnBsb3RfY2x1c3RlcnNfdmVydGljYWxfc3BhdGlhbF9ub19ncmlkKGp5XzQwOCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0zfQpjbHVzdGVycyA9IGxldmVscyhJZGVudHMoanlfNDA4KSkKcGxvdHMgPC0gbGFwcGx5KDE6bGVuZ3RoKGNsdXN0ZXJzKSwgZnVuY3Rpb24oaSl7CiAgICBwbG90X2NsdXN0ZXJzX3ZlcnRpY2FsX3NwYXRpYWwoanlfNDA4LCBjbHVzdGVyID0gY2x1c3RlcnNbaV0sIHB0LnNpemUgPSAwLjEpCiAgfSkKdmVydHM9IHBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RzLCBsYWJlbF9zaXplID0gMSwgbnJvdyA9IDEpCiN2ZXJ0cwojZ2dzYXZlKHBsb3QgPSB2ZXJ0cywgZmlsZW5hbWUgPSAnanlfNDA4X3NwYXRpYWxfYXJjX3NlcC5wZGYnLCBwYXRoID0gZmlsZS5wYXRoKG91dHB1dF9kaXJfcGxvdCwgJzIwMjIxMjEyXzEnKSwgd2lkdGggPSA3LCBoZWlnaHQgPSA3LCB1bml0cyA9ICdjbScsIGRwaSA9IDMwMCkKYGBgCgpgYGB7cn0Kb2RfaWRudHMgPSBjKCJNR0UiLCAiQ0dFL0xHRSIsICJUQlIxKyBDR0UiLCAiQ0FMQjIrRExYMisiLCAiVklQK0dBRDErIiwgIlNTVCtMSFg2KyIsICJFeCIpCnBhcCA9IHBsb3RfY2x1c3RlcnNfdmVydGljYWxfc3BhdGlhbF9ub19ncmlkKGp5XzQwOCwgcHQuc2l6ZSA9IDAuMSwgeF93aWR0aCA9IDQwLCBmb3JjZV9pZGVudHMgPSBvZF9pZG50cykKI2dnc2F2ZShwbG90ID0gcGFwLCBmaWxlbmFtZSA9ICdqeV80MDhfc3BhdGlhbF9hcmNfc2VwX29kLnBkZicsIHBhdGggPSBmaWxlLnBhdGgob3V0cHV0X2Rpcl9wbG90LCAnMjAyMjEyMTJfMScpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcsIHVuaXRzID0gJ2NtJywgZHBpID0gMzAwKQpwYXAKYGBgCgoK